home *** CD-ROM | disk | FTP | other *** search
/ Sprite 1984 - 1993 / Sprite 1984 - 1993.iso / src / machserver / 1.098 / fscache / fscacheBlocks.c < prev    next >
C/C++ Source or Header  |  1991-07-10  |  107KB  |  3,691 lines

  1. /* 
  2.  * fscacheBlocks.c --
  3.  *
  4.  *    Routines to manage the <file-id, block #> cache.
  5.  *
  6.  * Copyright 1987 Regents of the University of California.
  7.  * All rights reserved.
  8.  * Permission to use, copy, modify, and distribute this
  9.  * software and its documentation for any purpose and without
  10.  * fee is hereby granted, provided that the above copyright
  11.  * notice appear in all copies.  The University of California
  12.  * makes no representations about the suitability of this
  13.  * software for any purpose.  It is provided "as is" without
  14.  * express or implied warranty.
  15.  */
  16.  
  17. #ifndef lint
  18. static char rcsid[] = "$Header: /sprite/src/kernel/fscache/RCS/fscacheBlocks.c,v 9.23 91/06/27 12:34:40 mendel Exp Locker: mendel $ SPRITE (Berkeley)";
  19. #endif not lint
  20.  
  21. #include    <sprite.h>
  22. #include    <fs.h>
  23. #include    <fsutil.h>
  24. #include    <fscache.h>
  25. #include    <fscacheBlocks.h>
  26. #include    <fsStat.h>
  27. #include    <fsNameOps.h>
  28. #include    <fsdm.h>
  29. #include    <fsio.h>
  30. #include    <fsutilTrace.h>
  31. #include    <hash.h>
  32. #include    <vm.h>
  33. #include     <vmMach.h>
  34. #include    <proc.h>
  35. #include    <sys.h>
  36. #include    <rpc.h>
  37.  
  38.  
  39. /*
  40.  * There are numerous points of synchronization in this module.  The CacheInfo
  41.  * struct in the handle, the block info struct and several global conditions
  42.  * and variables are used.
  43.  *
  44.  *     1) Synchronization between a block being fetched and a block having
  45.  *      good data in it.  The FSCACHE_IO_IN_PROGRESS flag is used for this.
  46.  *      If this flag is set in the call to Fscache_FetchBlock then the fetch
  47.  *      will block until the block becomes unreferenced.  Thus if the block
  48.  *      is already being used the user won't get their data stomped on.
  49.  *        Whenever Fscache_FetchBlock returns a block it sets the 
  50.  *      FSCACHE_IO_IN_PROGRESS flag in the block.  This flag will be cleared
  51.  *      whenever the block is released with Fscache_UnlockBlock or the
  52.  *      function Fscache_IODone is called.  While this flag is set in the
  53.  *      block all future fetches will block until the flag is cleared.
  54.  *
  55.  *     2) Synchronization for changing where a block lives on disk.  When a
  56.  *      block cleaner is writing out a block and Fscache_UnlockBlock is
  57.  *      called with a new location for the block, it blocks until the
  58.  *      block cleaner finishes.  The flag FSCACHE_BLOCK_BEING_WRITTEN in the
  59.  *      cache block struct indicates this.
  60.  *
  61.  *     3) Waiting for blocks from a file to be written back.  This is done by
  62.  *      waiting for the dirty list for the file to go empty.
  63.  *
  64.  *     4) Waiting for blocks from the whole cache to be written back.  We
  65.  *      keep track of the number of cache backends starts and wake the
  66.  *      waiting process when this number goes to zero.
  67.  *
  68.  *     5) Synchronizing informing the server when there are no longer any
  69.  *      dirty blocks in the cache for a file.  When the last dirty block
  70.  *      for a file is written the write is tagged saying that it is the
  71.  *      last dirty block.  However the server is also told when a file is
  72.  *      closed if no dirty blocks are left.  Since all writes out of the
  73.  *      cache are unsynchronized there is a race between the close and the
  74.  *      delayed write back.  This is solved by using the following 
  75.  *      synchronization.  When a file is closed the function 
  76.  *      Fscache_PreventWriteBacks is called.  This function will not return
  77.  *      until there are no block cleaners active on the file.  When it
  78.  *      returns it sets the FSCACHE_CLOSE_IN_PROGRESS flag in the cacheInfo
  79.  *      struct in the file handle and it returns the number of dirty blocks.
  80.  *      All subsequent block writes are blocked until the function
  81.  *      Fscache_AllowWriteBacks is called.  Thus the number of dirty blocks
  82.  *      in the cache for the file is accurate on close because no dirty
  83.  *      blocks can be written out while the file is being closed.
  84.  *      Likewise when a block cleaner writes out the last dirty block for
  85.  *      a file and it tells the server on the write that its the last
  86.  *      dirty block the server knows that it can believe the block cleaner
  87.  *      if the file is closed.  This is because if its closed then it must
  88.  *      have been closed when the  block cleaner did the write
  89.  *      (all closes are prohibited during the write) and thus there
  90.  *      is no way that more dirty blocks can be put into the cache.
  91.  *      If its open then the server ignores what the block cleaner
  92.  *      says because it will get told again when the file is closed.
  93.  */
  94.  
  95. /*
  96.  * Monitor lock.
  97.  */
  98. static Sync_Lock    cacheLock = Sync_LockInitStatic("Fs:blockCacheLock");
  99. #define    LOCKPTR    &cacheLock
  100.  
  101. /*
  102.  * Condition variables.
  103.  */
  104. Sync_Condition    cleanBlockCondition;    /* Condition that block 
  105.                          * allocator waits on when all 
  106.                          * blocks are dirty. */
  107. Sync_Condition    writeBackComplete;     /* Condition to wait on when 
  108.                          * are waiting for the write 
  109.                          * back of blocks in the cache 
  110.                          * to complete. */
  111. Sync_Condition    closeCondition;        /* Condition to wait on when
  112.                          * are waiting for the block
  113.                          * cleaner to finish to write
  114.                          * out blocks for this file. */
  115. static unsigned int filewriteBackTime;        /* Write back all blocks in
  116.                          * the cache file 
  117.                          * descriptors that were
  118.                          * dirtied before this 
  119.                          * time. */
  120. static int    numBackendsActive;        /* Number of backend write back
  121.                              * processes currently active.
  122.                                  */
  123. /*
  124.  * Pointer to LRU list that is used for block allocation.
  125.  */
  126. static    List_Links    lruListHdr;
  127. #define    lruList        (&lruListHdr)
  128.  
  129. /*
  130.  * There are two free lists.  The first contains blocks that are in pages that
  131.  * only contain free blocks.  The second contains blocks that are in pages that
  132.  * contain non-free blocks.  When the physical pages size <= the block size then
  133.  * the second list will always be empty.
  134.  */
  135. static    List_Links    totFreeListHdr;
  136. #define    totFreeList    (&totFreeListHdr)
  137. static    List_Links    partFreeListHdr;
  138. #define    partFreeList    (&partFreeListHdr)
  139.  
  140. /*
  141.  * Pointer to list of unmapped blocks.
  142.  */
  143. static    List_Links    unmappedListHdr;
  144. #define    unmappedList    (&unmappedListHdr)
  145.  
  146. /*
  147.  * List of Fscache_Backend's that could have file in the cache.
  148.  * This list is kept so CacheWriteBack has a list of all the
  149.  * backends that could have files in the cache. 
  150.  */
  151. static    List_Links    backendListHdr;
  152. #define    backendList     (&backendListHdr)
  153.  
  154. /*
  155.  * Writes and reads can block on a full cache.  This list records the
  156.  * processes that are blocked on this condition.
  157.  */
  158. List_Links fscacheFullWaitListHdr;
  159. List_Links *fscacheFullWaitList = &fscacheFullWaitListHdr;
  160.  
  161. /*
  162.  * Hash tables for blocks.
  163.  */
  164. static    Hash_Table    blockHashTableStruct;
  165. static    Hash_Table    *blockHashTable = &blockHashTableStruct;
  166.  
  167. /*
  168.  * The key to use for the block hash and a macro to set it.  The fact
  169.  * that the key includes a pointer into the I/O handle for the block
  170.  * means that this handle has to be kept around until there are no
  171.  * blocks left in the cache.
  172.  */
  173. typedef    struct {
  174.     Fscache_FileInfo *cacheInfoPtr;
  175.     int        blockNumber;
  176. } BlockHashKey;
  177. #define    SET_BLOCK_HASH_KEY(blockHashKey, ZcacheInfoPtr, fileBlock) \
  178.     (blockHashKey).cacheInfoPtr = ZcacheInfoPtr; \
  179.     (blockHashKey).blockNumber = fileBlock;
  180.  
  181. /*
  182.  * Miscellaneous variables.
  183.  */
  184. static    Address    blockCacheStart;    /* The address of the beginning of the
  185.                           block cache. */
  186. static    Address    blockCacheEnd;        /* The maximum virtual address for the 
  187.                      * cache.*/
  188. static    int    pageSize;        /* The size of a physical page. */
  189. static    int    blocksPerPage;        /* Number of blocks in a page. */
  190.  
  191. static  int    numAvailBlocks;        /* Number of cache block available for 
  192.                      * use without waiting. */
  193. static  int    minNumAvailBlocks;    /* Minimum number of cache blocks to
  194.                      * keep available. */
  195.  
  196. /*
  197.  * Macros for large page sizes.
  198.  *
  199.  *    GET_OTHER_BLOCK        Return a pointer to the other block in
  200.  *                the page given a pointer to one of the blocks.
  201.  *    PAGE_IS_8K        Return true if the VM page size is 8K.
  202.  */
  203. #define GET_OTHER_BLOCK(blockPtr) \
  204.     (((int) blockPtr->blockAddr & (pageSize - 1)) != 0) ?  \
  205.         blockPtr - 1 : blockPtr + 1
  206.  
  207. #define    PAGE_IS_8K    (pageSize == 8192)
  208.  
  209. /*
  210.  * External symbols.
  211.  */
  212.  
  213. int    fscache_MaxBlockCleaners = FSCACHE_MAX_CLEANER_PROCS;
  214.  
  215. /*
  216.  * Internal functions.
  217.  */
  218. static void CacheFileInvalidate _ARGS_((Fscache_FileInfo *cacheInfoPtr, 
  219.             int firstBlock, int lastBlock));
  220. static void DeleteBlockFromDirtyList _ARGS_((Fscache_Block *blockPtr));
  221. static void StartFileSync _ARGS_((Fscache_FileInfo *cacheInfoPtr));
  222. static void CacheWriteBack _ARGS_((unsigned int writeBackTime, 
  223.             int *blocksSkippedPtr, Boolean writeTmpFiles));
  224. static Boolean CreateBlock _ARGS_((Boolean retBlock,
  225.             Fscache_Block **blockPtrPtr));
  226. static Boolean DestroyBlock _ARGS_((Boolean retOnePage, int *pageNumPtr));
  227. #ifdef SOSP91
  228. static Fscache_Block *FetchBlock _ARGS_((Boolean canWait, Boolean cantBlock,
  229.             unsigned int flags));
  230. #else
  231. static Fscache_Block *FetchBlock _ARGS_((Boolean canWait, Boolean cantBlock));
  232. #endif /* SOSP91 */
  233. static void StartBackendWriteback _ARGS_((Fscache_Backend *backendPtr));
  234. static void PutOnFreeList _ARGS_((Fscache_Block *blockPtr));
  235. static void PutFileOnDirtyList _ARGS_((Fscache_FileInfo *cacheInfoPtr,
  236.             time_t oldestDirtyBlockTime));
  237. static void PutBlockOnDirtyList _ARGS_((Fscache_Block *blockPtr, 
  238.             Boolean onFront));
  239. static Hash_Entry *GetUnlockedBlock _ARGS_((BlockHashKey *blockHashKeyPtr, 
  240.             int blockNum));
  241. static void DeleteBlock _ARGS_((Fscache_Block *blockPtr));
  242.  
  243.  
  244. /*
  245.  * ----------------------------------------------------------------------------
  246.  *
  247.  * Fscache_Init --
  248.  *
  249.  *     Initialize the cache.
  250.  *
  251.  * Results:
  252.  *    None.
  253.  *
  254.  * Side effects:
  255.  *    Hash tables initialized and memory allocated for the cache.
  256.  *
  257.  * ----------------------------------------------------------------------------
  258.  */
  259. void
  260. Fscache_Init(blockHashSize)
  261.     int    blockHashSize;    /* The number of hash table entries to put in the
  262.                block hash table for starters. */
  263. {
  264.     register    Address        blockAddr;
  265.     Address            listStart;
  266.     register    Fscache_Block    *blockPtr;
  267.     register    int        i;
  268.  
  269.     Vm_FsCacheSize(&blockCacheStart, &blockCacheEnd);
  270.     pageSize = Vm_GetPageSize();
  271.     blocksPerPage = pageSize / FS_BLOCK_SIZE;
  272.     /*
  273.      * Currently the cache code only handles the case for 
  274.      * (pageSize != FS_BLOCK_SIZE) when the block size is 4K and 
  275.      * the page size 8K.
  276.      */
  277.     if (!((pageSize == FS_BLOCK_SIZE) ||
  278.       ((FS_BLOCK_SIZE == 4096) && PAGE_IS_8K))) {
  279.     panic("Bad pagesize (%d) for file cache code\n", pageSize);
  280.     }
  281.     fs_Stats.blockCache.maxNumBlocks = 
  282.             (blockCacheEnd - blockCacheStart + 1) / FS_BLOCK_SIZE;
  283.  
  284.     fs_Stats.blockCache.minCacheBlocks = FSCACHE_MIN_BLOCKS;
  285.     fs_Stats.blockCache.maxCacheBlocks = fs_Stats.blockCache.maxNumBlocks;
  286.  
  287.     /*
  288.      * Allocate space for the cache block list.
  289.      */
  290.     listStart = Vm_RawAlloc((int)fs_Stats.blockCache.maxNumBlocks *
  291.                 sizeof(Fscache_Block));
  292.     blockPtr = (Fscache_Block *) listStart;
  293.  
  294.     /*
  295.      * Initialize the hash table.
  296.      */
  297.     Hash_Init(blockHashTable, blockHashSize, Hash_Size(sizeof(BlockHashKey)));
  298.  
  299.     /*
  300.      * Initialize all lists.
  301.      */
  302.     List_Init(lruList);
  303.     List_Init(totFreeList);
  304.     List_Init(partFreeList);
  305.     List_Init(backendList);
  306.     List_Init(unmappedList);
  307.     List_Init(fscacheFullWaitList);
  308.  
  309.     for (i = 0, blockAddr = blockCacheStart, 
  310.         blockPtr = (Fscache_Block *)listStart;
  311.      i < fs_Stats.blockCache.maxNumBlocks; 
  312.      i++, blockPtr++, blockAddr += FS_BLOCK_SIZE) {
  313.     blockPtr->flags = FSCACHE_NOT_MAPPED;
  314.     blockPtr->blockAddr = blockAddr;
  315.     blockPtr->refCount = 0;
  316.     List_Insert(&blockPtr->useLinks, LIST_ATREAR(unmappedList));
  317.     }
  318. #ifdef sun4
  319.     {
  320.     /*
  321.      * Hack that allows VM to be backward compat with old block cache
  322.      * code.  This should have been removed by the time you see it.
  323.      */
  324.     extern Boolean vmMachCanStealFileCachePmegs;
  325.     vmMachCanStealFileCachePmegs = TRUE;
  326.     }
  327. #endif
  328.     /*
  329.      * Give enough blocks memory so that the minimum cache size requirement
  330.      * is met.
  331.      */
  332.     fs_Stats.blockCache.numCacheBlocks = 0;
  333.     while (fs_Stats.blockCache.numCacheBlocks < 
  334.                     fs_Stats.blockCache.minCacheBlocks) {
  335.     if (!CreateBlock(FALSE, (Fscache_Block **) NIL)) {
  336.         printf("Fscacahe_Init: Couldn't create block\n");
  337.         fs_Stats.blockCache.minCacheBlocks = 
  338.                     fs_Stats.blockCache.numCacheBlocks;
  339.     }
  340.     }
  341.     minNumAvailBlocks = fs_Stats.blockCache.minCacheBlocks/2;
  342.     printf("FS Cache has %d %d-Kbyte blocks (%d max)\n",
  343.         fs_Stats.blockCache.minCacheBlocks, FS_BLOCK_SIZE / 1024,
  344.         fs_Stats.blockCache.maxNumBlocks);
  345.  
  346. }
  347.  
  348.  
  349. /*
  350.  * ----------------------------------------------------------------------------
  351.  *
  352.  * Fscache_FileInfoInit --
  353.  *
  354.  *     Initialize the cache information for a file.  Called when setting
  355.  *    up a handle for a file that uses the block cache.
  356.  *
  357.  * Results:
  358.  *    None.
  359.  *
  360.  * Side effects:
  361.  *    Set up the fields of the Fscache_FileInfo struct. First file may
  362.  *    cause the backend to be initialized.
  363.  *    
  364.  * ----------------------------------------------------------------------------
  365.  */
  366. void
  367. Fscache_FileInfoInit(cacheInfoPtr, hdrPtr, version, cacheable, attrPtr, 
  368.         backendPtr)
  369.     register Fscache_FileInfo *cacheInfoPtr;    /* Information to initialize. */
  370.     Fs_HandleHeader         *hdrPtr;        /* Back pointer to handle */
  371.     int                 version;        /* Used to check consistency */
  372.     Boolean             cacheable;        /* TRUE if server says we can
  373.                          * cache */
  374.     Fscache_Attributes         *attrPtr;        /* File attributes */
  375.     Fscache_Backend         *backendPtr;    /* Cache backend for
  376.                          * this file. */
  377. {
  378.     List_InitElement(&cacheInfoPtr->links);
  379.     List_Init(&cacheInfoPtr->dirtyList);
  380.     List_Init(&cacheInfoPtr->blockList);
  381.     List_Init(&cacheInfoPtr->indList);
  382.     cacheInfoPtr->flags = (cacheable ? 0 : FSCACHE_FILE_NOT_CACHEABLE);
  383.     cacheInfoPtr->version = version;
  384.     cacheInfoPtr->hdrPtr = hdrPtr;
  385.     cacheInfoPtr->blocksWritten = 0;
  386.     cacheInfoPtr->noDirtyBlocks.waiting = 0;
  387.     cacheInfoPtr->blocksInCache = 0;
  388.     cacheInfoPtr->numDirtyBlocks = 0;
  389.     cacheInfoPtr->lastTimeTried = 0;
  390.     cacheInfoPtr->oldestDirtyBlockTime = Fsutil_TimeInSeconds();
  391.     cacheInfoPtr->attr = *attrPtr;
  392.     cacheInfoPtr->backendPtr = backendPtr;
  393.     Sync_LockInitDynamic(&cacheInfoPtr->lock, "Fs:perFileCacheLock");
  394. }
  395.  
  396. /*
  397.  * ----------------------------------------------------------------------------
  398.  *
  399.  * Fscache_InfoSyncLockCleanup --
  400.  *
  401.  *     Clean up Sync_Lock tracing for the cache lock.
  402.  *
  403.  * Results:
  404.  *    None.
  405.  *
  406.  * Side effects:
  407.  *    Set up the fields of the Fscache_FileInfo struct.
  408.  *
  409.  * ----------------------------------------------------------------------------
  410.  */
  411. /*ARGSUSED*/
  412. void
  413. Fscache_InfoSyncLockCleanup(cacheInfoPtr)
  414.     Fscache_FileInfo *cacheInfoPtr;
  415. {
  416.     Sync_LockClear(&cacheInfoPtr->lock);
  417. }
  418.  
  419.  
  420.  
  421. /*
  422.  *----------------------------------------------------------------------
  423.  *
  424.  * Fscache_RegisterBackend --
  425.  *
  426.  *    Allocate and initialize the Fscache_Backend data structure for a 
  427.  *    cache backend.
  428.  *
  429.  * Results:
  430.  *    The malloc'ed cache backend.
  431.  *
  432.  * Side effects:
  433.  *    The backend is added to the backendList.
  434.  *
  435.  *----------------------------------------------------------------------
  436.  */
  437.  
  438. Fscache_Backend *
  439. Fscache_RegisterBackend(ioProcsPtr, clientData, flags)
  440.     Fscache_BackendRoutines   *ioProcsPtr;
  441.     ClientData    clientData;
  442.     int         flags;    /* Backend flags. */
  443. {
  444.     Fscache_Backend    *backendPtr;
  445.  
  446.     LOCK_MONITOR;
  447.  
  448.     backendPtr = (Fscache_Backend *) malloc(sizeof(Fscache_Backend));
  449.     bzero((char *) backendPtr, sizeof(Fscache_Backend));
  450.  
  451.     List_Init((List_Links *)backendPtr);
  452.     backendPtr->clientData = clientData;
  453.     backendPtr->flags = flags;
  454.     List_Init((List_Links *)&(backendPtr->dirtyListHdr));
  455.     backendPtr->ioProcs = *ioProcsPtr;
  456.  
  457.     List_Insert((List_Links *)backendPtr, LIST_ATREAR(backendList));
  458.     UNLOCK_MONITOR;
  459.     return backendPtr;
  460. }
  461.  
  462. /*
  463.  *----------------------------------------------------------------------
  464.  *
  465.  * Fscache_UnregisterBackend --
  466.  *
  467.  *    Deallocate a Fscache_Backend data structure allocated with
  468.  *    Fscache_RegisterBackend.
  469.  *
  470.  * Results:
  471.  *
  472.  * Side effects:
  473.  *    Backend is removed from the list of active backends and its memory
  474.  *    is freed.
  475.  *
  476.  *----------------------------------------------------------------------
  477.  */
  478.  
  479. void
  480. Fscache_UnregisterBackend(backendPtr)
  481.     Fscache_Backend    *backendPtr; /* Backend to deallocate. */
  482. {
  483.  
  484.     LOCK_MONITOR;
  485.  
  486.     if (!List_IsEmpty(&(backendPtr->dirtyListHdr))) {
  487.     UNLOCK_MONITOR;
  488.     panic("Fscache_UnregisterBackend: Backend has dirty files.\n");
  489.     return;
  490.     }
  491.     List_Remove((List_Links *)backendPtr);
  492.     /*
  493.      * Until we get a better grip on the file handle code we can't free
  494.      * the backendPtr because the cacheInfoPtr in the handle may still
  495.      * point at it.  
  496.      *
  497.      * THIS IS A MEMORY LEAK - 48 bytes per backend unregistered. 
  498.      * Currently this only happens when an LFS file system is 
  499.      * unattached.
  500.      */
  501.  
  502. #ifdef notdef
  503. #ifndef CLEAN
  504.     /*
  505.      * NIL out these fields in hope in catching any bad code that
  506.      * trys to use them.
  507.      */
  508.     backendPtr->clientData = (ClientData) NIL;
  509.     backendPtr->ioProcs.allocate = (ReturnStatus (*)()) NIL;
  510.     backendPtr->ioProcs.blockRead = (ReturnStatus (*)()) NIL;
  511.     backendPtr->ioProcs.blockWrite = (ReturnStatus (*)()) NIL;
  512.     backendPtr->ioProcs.reallocBlock = (void (*)()) NIL;
  513.     backendPtr->ioProcs.startWriteBack = (ReturnStatus (*)()) NIL;
  514. #endif /* not CLEAN */
  515.  
  516.     free((char *) backendPtr);
  517. #endif /* notdef */
  518.  
  519.     UNLOCK_MONITOR;
  520.     return;
  521. }
  522.  
  523.  
  524.  
  525. /*
  526.  * ----------------------------------------------------------------------------
  527.  *
  528.  *    Functions for external block access.  Includes functions to fetch,
  529.  *    release and truncate cache blocks.
  530.  *
  531.  * ----------------------------------------------------------------------------
  532.  */
  533.  
  534. /*
  535.  * ----------------------------------------------------------------------------
  536.  *
  537.  * Fscache_FetchBlock --
  538.  *
  539.  *    Return in *blockPtrPtr a pointer to a block in the 
  540.  *    cache that corresponds to virtual block blockNum in the file 
  541.  *    identified by *cacheInfoPtr.  If the block for the file is not in the 
  542.  *    cache then *foundPtr is set to FALSE and if allocate is 
  543.  *    TRUE, then a clean block is returned.  Otherwise a pointer to the 
  544.  *    actual data block is returned and *foundPtr is set to TRUE.
  545.  *    The block that is returned is locked down in the cache (i.e. it cannot
  546.  *    be replaced) until it is unlocked by Fscache_UnlockBlock.  If the block
  547.  *    isn't found or the FSCACHE_IO_IN_PROGRESS flag is given then the block
  548.  *    is marked as IO in progress and must be either unlocked by 
  549.  *    Fscache_UnlockBlock or marked as IO done by Fscache_IODone.
  550.  *
  551.  * Results:
  552.  *    None.
  553.  *
  554.  * Side effects:
  555.  *    New block may be allocated out of the block cache, block that 
  556.  *    is returned is locked, and the block may be set as IO in progress.
  557.  *
  558.  * ----------------------------------------------------------------------------
  559.  *
  560.  */
  561. static Fscache_Block *lostBlockPtr;
  562.  
  563. ENTRY void
  564. Fscache_FetchBlock(cacheInfoPtr, blockNum, flags, blockPtrPtr, foundPtr)
  565.     register Fscache_FileInfo *cacheInfoPtr; /* Pointer to the cache state 
  566.                    * for the file. */
  567.     int         blockNum;    /* Virtual block number in the file. */
  568.     int         flags;        /* FSCACHE_DONT_BLOCK |
  569.                  * FSCACHE_CANT_BLOCK |
  570.                  * FSCACHE_READ_AHEAD_BLOCK |
  571.                  * FSCACHE_IO_IN_PROGRESS
  572.                  * plus the type of block */
  573.     Fscache_Block **blockPtrPtr; /* Where pointer to cache block information
  574.                  * structure is returned. The structure
  575.                  * contains the virtual address of the 
  576.                  * actual cache block. */
  577.     Boolean    *foundPtr;    /* TRUE if the block is present in the
  578.                  * cache, FALSE if not. */
  579. {
  580.     BlockHashKey        blockHashKey;
  581.     register    Hash_Entry    *hashEntryPtr;
  582.     register    Fscache_Block    *blockPtr;
  583.     Fscache_Block        *otherBlockPtr;
  584.     Fscache_Block        *newBlockPtr;
  585.     time_t            refTime;
  586.     Boolean        cantBlock = (flags & FSCACHE_CANT_BLOCK);
  587.     Boolean        dontBlock = (flags & FSCACHE_DONT_BLOCK);
  588.  
  589.     LOCK_MONITOR;
  590.  
  591.     *blockPtrPtr = (Fscache_Block *)NIL;
  592.     SET_BLOCK_HASH_KEY(blockHashKey, cacheInfoPtr, blockNum);
  593.  
  594.     do {
  595.     /*
  596.      * Keep re-hashing until we get a block.  If we ever have to
  597.      * wait in this loop then the hash table can change out from
  598.      * under us, so we always rehash.
  599.      */
  600.     hashEntryPtr = Hash_Find(blockHashTable, (Address) &blockHashKey);
  601.     blockPtr = (Fscache_Block *) Hash_GetValue(hashEntryPtr);
  602.     if (blockPtr != (Fscache_Block *) NIL) {
  603.         Boolean    blockBusy;
  604.         *foundPtr = TRUE;
  605.         if (blockPtr->fileNum != cacheInfoPtr->hdrPtr->fileID.minor) {
  606.         UNLOCK_MONITOR;
  607.         panic("Fscache_FetchBlock hashing error\n");
  608.         *foundPtr = FALSE;
  609.         return;
  610.         }
  611.         blockBusy = ((blockPtr->refCount > 0) || 
  612.             (blockPtr->flags & (FSCACHE_IO_IN_PROGRESS|
  613.                     FSCACHE_BLOCK_BEING_WRITTEN)));
  614.         if ( ((flags & FSCACHE_IO_IN_PROGRESS) && blockBusy) ||
  615.           (blockPtr->flags & FSCACHE_IO_IN_PROGRESS)) {
  616.         if (!dontBlock) {
  617.             /*
  618.              * Wait until it becomes unlocked, or return
  619.              * found = TRUE and block = NIL if caller can't block.
  620.              */
  621.             (void)Sync_Wait(&blockPtr->ioDone, FALSE);
  622.         }
  623.         blockPtr = (Fscache_Block *)NIL;
  624.         } else {
  625.         blockPtr->refCount++;
  626.         if (blockPtr->refCount == 1) {
  627.             VmMach_LockCachePage(blockPtr->blockAddr);
  628.             if (!(blockPtr->flags & FSCACHE_BLOCK_DIRTY)) {
  629.             numAvailBlocks--;
  630.             if (numAvailBlocks < 0) {
  631.                 panic("Fscache_FetchBlock numAvailBlocks < 0\n");
  632.             }
  633.             }
  634.         }
  635.         if (flags & FSCACHE_IO_IN_PROGRESS) {
  636.             blockPtr->flags |= FSCACHE_IO_IN_PROGRESS;
  637.         }
  638.         }
  639.     } else {
  640.         /*
  641.          * Have to allocate a block.  If there is a free block use it, or
  642.          * take a block off of the lru list, or make a new one.
  643.          */
  644.         *foundPtr = FALSE;
  645.         if ((numAvailBlocks > minNumAvailBlocks) || cantBlock) {
  646.         /*
  647.          * If we have enought blocks available take a free one.
  648.          */
  649.         if (!List_IsEmpty(partFreeList)) {
  650.             /*
  651.              * Use partially free blocks first.
  652.              */
  653.             fs_Stats.blockCache.numFreeBlocks--;
  654.             fs_Stats.blockCache.partFree++;
  655.             blockPtr = USE_LINKS_TO_BLOCK(List_First(partFreeList));
  656.             List_Remove(&blockPtr->useLinks);
  657.         } else if (!List_IsEmpty(totFreeList)) {
  658.             /*
  659.              * Can't find a partially free block so use a totally free
  660.              * block.
  661.              */
  662.             fs_Stats.blockCache.numFreeBlocks--;
  663.             fs_Stats.blockCache.totFree++;
  664.             blockPtr = USE_LINKS_TO_BLOCK(List_First(totFreeList));
  665.             List_Remove(&blockPtr->useLinks);
  666.             if (PAGE_IS_8K) {
  667.             otherBlockPtr = GET_OTHER_BLOCK(blockPtr);
  668.             List_Move(&otherBlockPtr->useLinks,
  669.                       LIST_ATREAR(partFreeList));
  670.             }
  671.         }
  672.         }
  673.         if (blockPtr == (Fscache_Block *) NIL) {
  674.         /*
  675.          * Can't find any free blocks so have to use one of our blocks
  676.          * or create new ones.
  677.          */
  678.         if (fs_Stats.blockCache.numCacheBlocks >= 
  679.                     fs_Stats.blockCache.maxCacheBlocks) {
  680.             /*
  681.              * We can't have anymore blocks so reuse one of our own.
  682.              */
  683. #ifdef SOSP91
  684.             blockPtr = FetchBlock(!dontBlock, cantBlock,
  685.                 FSCACHE_BLOCK_LRU);
  686. #else
  687.             blockPtr = FetchBlock(!dontBlock, cantBlock);
  688. #endif /* SOSP91 */
  689.             if ((blockPtr == (Fscache_Block *) NIL) && cantBlock) {
  690.             goto getBlock;
  691.             }
  692.         } else {
  693.             /*
  694.              * Grow the cache if VM has an older page than we have.
  695.              */
  696.             refTime = Vm_GetRefTime();
  697.             blockPtr = USE_LINKS_TO_BLOCK(List_First(lruList));
  698.             if (blockPtr->timeReferenced > refTime) {
  699.         getBlock:
  700.             if (!CreateBlock(TRUE, &newBlockPtr)) {
  701. #ifdef SOSP91
  702.                 blockPtr = FetchBlock(!dontBlock, cantBlock,
  703.                     FSCACHE_BLOCK_LRU);
  704. #else
  705.                 blockPtr = FetchBlock(!dontBlock, cantBlock);
  706. #endif /* SOSP91 */
  707.             } else {
  708.                 fs_Stats.blockCache.unmapped++;
  709.                 blockPtr = newBlockPtr;
  710.             }
  711.             } else {
  712.             /*
  713.              * We have an older block than VM's oldest page so reuse
  714.              * the block.
  715.              */
  716. #ifdef SOSP91
  717.             blockPtr = FetchBlock(!dontBlock, cantBlock,
  718.                 FSCACHE_BLOCK_LRU);
  719. #else
  720.             blockPtr = FetchBlock(!dontBlock, cantBlock);
  721. #endif /* SOSP91 */
  722.             }
  723.         }
  724.         }
  725.         /*
  726.          * If blockPtr is NIL we waited for room in the cache or
  727.          * for a busy cache block.  Now we'll retry all the various
  728.          * ploys to get a free block.
  729.          */
  730.     }
  731.     } while ((blockPtr == (Fscache_Block *)NIL) && !dontBlock);
  732.  
  733.     if ((*foundPtr == FALSE) && (blockPtr != (Fscache_Block *)NIL)) {
  734.     cacheInfoPtr->blocksInCache++;
  735.     blockPtr->cacheInfoPtr = cacheInfoPtr;
  736.     blockPtr->refCount = 1;
  737.     VmMach_LockCachePage(blockPtr->blockAddr);
  738.     blockPtr->flags = flags & (FSCACHE_DATA_BLOCK | FSCACHE_IND_BLOCK |
  739.                    FSCACHE_DESC_BLOCK | FSCACHE_DIR_BLOCK |
  740.                    FSCACHE_READ_AHEAD_BLOCK);
  741.     blockPtr->flags |= FSCACHE_IO_IN_PROGRESS;
  742.     blockPtr->fileNum = cacheInfoPtr->hdrPtr->fileID.minor;
  743.     blockPtr->blockNum = blockNum;
  744.     blockPtr->blockSize = -1;
  745.     blockPtr->timeDirtied = 0;
  746.     blockPtr->timeReferenced = Fsutil_TimeInSeconds();
  747.     *blockPtrPtr = blockPtr;
  748.     if (Hash_GetValue(hashEntryPtr) != (char *)NIL) {
  749.         lostBlockPtr = (Fscache_Block *)Hash_GetValue(hashEntryPtr);
  750.         UNLOCK_MONITOR;
  751.         panic("Fscache_FetchBlock: hashEntryPtr->value changed\n");
  752.         LOCK_MONITOR;
  753.     }
  754.     Hash_SetValue(hashEntryPtr, blockPtr);
  755.     List_Insert(&(blockPtr->useLinks), LIST_ATREAR(lruList));
  756.     List_InitElement(&blockPtr->fileLinks);
  757.     if (flags & FSCACHE_IND_BLOCK) {
  758.         List_Insert(&blockPtr->fileLinks, LIST_ATREAR(&cacheInfoPtr->indList));
  759.     } else {
  760.         List_Insert(&blockPtr->fileLinks,LIST_ATREAR(&cacheInfoPtr->blockList));
  761.     }
  762.     numAvailBlocks--;
  763.     if (numAvailBlocks < 0) {
  764.         panic("Fscache_FetchBlock numAvailBlocks < 0.\n");
  765.     }
  766.     }
  767.     *blockPtrPtr = blockPtr;
  768.     UNLOCK_MONITOR;
  769.     return;
  770. }
  771.  
  772.  
  773. /*
  774.  * ----------------------------------------------------------------------------
  775.  *
  776.  * Fscache_IODone --
  777.  *
  778.  *    Remove the IO-in-progress flag from the cache block flags field.
  779.  *
  780.  * Results:
  781.  *    None.
  782.  *
  783.  * Side effects:
  784.  *    The IO-in-progress flag is removed from the cache block flags field.
  785.  *
  786.  * ----------------------------------------------------------------------------
  787.  *
  788.  */
  789. ENTRY void
  790. Fscache_IODone(blockPtr)
  791.     Fscache_Block *blockPtr;    /* Pointer to block information for block.*/
  792. {
  793.     LOCK_MONITOR;
  794.  
  795.     Sync_Broadcast(&blockPtr->ioDone);
  796.     blockPtr->flags &= ~FSCACHE_IO_IN_PROGRESS;
  797.  
  798.     UNLOCK_MONITOR;
  799. }
  800.  
  801. /*
  802.  * ----------------------------------------------------------------------------
  803.  *
  804.  * Fscache_UnlockBlock --
  805.  *
  806.  *    Release the lock on the cache block pointed to by blockPtr.
  807.  *
  808.  * Results:
  809.  *    None.
  810.  *
  811.  * Side effects:
  812.  *    The lock count of the block is decremented.
  813.  *
  814.  * ----------------------------------------------------------------------------
  815.  *
  816.  */
  817. ENTRY void
  818. Fscache_UnlockBlock(blockPtr, timeDirtied, diskBlock, blockSize, flags)
  819.     Fscache_Block *blockPtr;    /* Pointer to block information for block
  820.                    that is to be released. */
  821.     time_t     timeDirtied;    /* Time in seconds that the block was 
  822.                    dirtied. */
  823.     int         diskBlock;    /* If not -1 is the block on disk where this
  824.                    block resides.  For remote blocks this 
  825.                    should be the same as blockPtr->blockNum.*/
  826.     int         blockSize;    /* The number of valid bytes in this block. */
  827.     int         flags;        /* FSCACHE_DELETE_BLOCK | FSCACHE_CLEAR_READ_AHEAD |
  828.                  * FSCACHE_BLOCK_UNNEEDED | FSCACHE_DONT_WRITE_THRU
  829.                  * FSCACHE_WRITE_TO_DISK | FSCACHE_BLOCK_BEING_CLEANED*/
  830. {
  831.     LOCK_MONITOR;
  832.  
  833.     if (blockPtr->flags & FSCACHE_BLOCK_FREE) {
  834.     panic("Checking in free block\n");
  835.     }
  836.  
  837.     if (blockPtr->flags & FSCACHE_IO_IN_PROGRESS) {
  838.     Sync_Broadcast(&blockPtr->ioDone);
  839.     blockPtr->flags &= ~FSCACHE_IO_IN_PROGRESS;
  840.     }
  841.  
  842.     if (flags & FSCACHE_DELETE_BLOCK) {
  843.     /*
  844.      * The caller is deleting this block from the cache.  Decrement the
  845.      * lock count and then invalidate the block.
  846.      */
  847.     blockPtr->refCount--;
  848.     if (blockPtr->refCount == 0) { 
  849.         VmMach_UnlockCachePage(blockPtr->blockAddr);
  850.         if (!(blockPtr->flags & 
  851.             (FSCACHE_BLOCK_DIRTY|FSCACHE_BLOCK_BEING_WRITTEN))) {
  852.         numAvailBlocks++;
  853.         if (! List_IsEmpty(fscacheFullWaitList)) {
  854.             Fsutil_WaitListNotify(fscacheFullWaitList);
  855.         }
  856.         Sync_Broadcast(&cleanBlockCondition);
  857.         }
  858.     }
  859.     CacheFileInvalidate(blockPtr->cacheInfoPtr, blockPtr->blockNum, 
  860.                 blockPtr->blockNum);
  861.     UNLOCK_MONITOR;
  862.     return;
  863.     }
  864.  
  865.     if (flags & FSCACHE_CLEAR_READ_AHEAD) {
  866.     blockPtr->flags &= ~FSCACHE_READ_AHEAD_BLOCK;
  867.     }
  868.     if (timeDirtied != 0) {
  869.     if (flags & FSCACHE_BLOCK_BEING_CLEANED) {
  870.         blockPtr->cacheInfoPtr->flags |= FSCACHE_FILE_BEING_CLEANED;
  871.         blockPtr->flags |= FSCACHE_BLOCK_BEING_CLEANED;
  872.     }
  873.     if (!(blockPtr->flags & FSCACHE_BLOCK_DIRTY)) {
  874.         /*
  875.          * Increment the count of dirty blocks if the block isn't marked
  876.          * as dirty.  The block cleaner will decrement the count 
  877.          * after it cleans a block.
  878.          */
  879.         blockPtr->flags |= FSCACHE_BLOCK_DIRTY;
  880.         blockPtr->timeDirtied = timeDirtied;
  881.         if (!(blockPtr->flags & FSCACHE_BLOCK_BEING_WRITTEN)) { 
  882.         blockPtr->cacheInfoPtr->numDirtyBlocks++;
  883.         PutBlockOnDirtyList(blockPtr, FALSE);
  884.         }
  885.     }
  886.     }
  887.     if (diskBlock != -1) {
  888.     blockPtr->diskBlock = diskBlock;
  889.     if (blockPtr->blockSize != blockSize) {
  890.         blockPtr->blockSize = blockSize;
  891.     }
  892.     } else if (blockPtr->blockSize == -1 && blockSize > 0) {
  893.     /*
  894.      * Patch up the block size so our internal fragmentation
  895.      * calculation is correct.  The size of a read-only block
  896.      * is not used for anything else.
  897.      */
  898.     blockPtr->blockSize = blockSize;
  899.     }
  900.  
  901.     blockPtr->refCount--;
  902.     if (blockPtr->refCount == 0) {
  903.     /*
  904.      * Wake up anybody waiting for the block to become unlocked.
  905.      */
  906.     Sync_Broadcast(&blockPtr->ioDone);
  907.     VmMach_UnlockCachePage(blockPtr->blockAddr);
  908.     if (!(blockPtr->flags & 
  909.         (FSCACHE_BLOCK_DIRTY | FSCACHE_BLOCK_BEING_WRITTEN))) {
  910.         numAvailBlocks++;
  911.         if (! List_IsEmpty(fscacheFullWaitList)) {
  912.         Fsutil_WaitListNotify(fscacheFullWaitList);
  913.         }
  914.         Sync_Broadcast(&cleanBlockCondition);
  915.     }
  916.     if (blockPtr->flags & FSCACHE_BLOCK_CLEANER_WAITING) {
  917.         StartBackendWriteback(blockPtr->cacheInfoPtr->backendPtr);
  918.         blockPtr->flags &= ~FSCACHE_BLOCK_CLEANER_WAITING;
  919.     }
  920.     if (flags & FSCACHE_BLOCK_UNNEEDED) {
  921.         /*
  922.          * This block is unneeded so move it to the front of the LRU list
  923.          * and set its time referenced to zero so that it will be taken
  924.          * at the next convenience.
  925.          */
  926.         if (blockPtr->flags & FSCACHE_BLOCK_DIRTY) {
  927.         blockPtr->flags |= FSCACHE_MOVE_TO_FRONT;
  928.         } else {
  929.         List_Move(&blockPtr->useLinks, LIST_ATFRONT(lruList));
  930.         }
  931.         fs_Stats.blockCache.blocksPitched++;
  932.         blockPtr->timeReferenced = 0;
  933.     } else {
  934.         /*
  935.          * Move it to the end of the lru list, mark it as being referenced. 
  936.          */
  937.         blockPtr->timeReferenced = Fsutil_TimeInSeconds();
  938.         blockPtr->flags &= ~FSCACHE_MOVE_TO_FRONT;
  939.         List_Move( &blockPtr->useLinks, LIST_ATREAR(lruList));
  940.     }
  941.     }
  942.  
  943.     UNLOCK_MONITOR;
  944. }
  945.  
  946.  
  947. /*
  948.  * ----------------------------------------------------------------------------
  949.  *
  950.  * Fscache_BlockTrunc --
  951.  *
  952.  *     Truncate the given cache block.  Used to set the blockSize in the
  953.  *    cache block to reflect the actual amount of data in the block after
  954.  *    a truncate.
  955.  *
  956.  * Results:
  957.  *    None.
  958.  *
  959.  * Side effects:
  960.  *    blockSize in block is changed.
  961.  *
  962.  * ----------------------------------------------------------------------------
  963.  */
  964. ENTRY void
  965. Fscache_BlockTrunc(cacheInfoPtr, blockNum, newBlockSize)
  966.     Fscache_FileInfo *cacheInfoPtr;    /* Cache state of file. */
  967.     int        blockNum;        /* Block to truncate. */
  968.     int        newBlockSize;        /* New block size. */
  969. {
  970.     register Hash_Entry         *hashEntryPtr;
  971.     register Fscache_Block    *blockPtr;
  972.     BlockHashKey         blockHashKey;
  973.  
  974.     LOCK_MONITOR;
  975.  
  976.     SET_BLOCK_HASH_KEY(blockHashKey, cacheInfoPtr, 0);
  977.  
  978.     hashEntryPtr = GetUnlockedBlock(&blockHashKey, blockNum);
  979.     if (hashEntryPtr != (Hash_Entry *) NIL) {
  980.     blockPtr = (Fscache_Block *) Hash_GetValue(hashEntryPtr);
  981.  
  982.     if (blockPtr->fileNum != cacheInfoPtr->hdrPtr->fileID.minor) {
  983.         panic( "CacheBlockTrunc, hashing error\n");
  984.     } else {
  985.         blockPtr->blockSize = newBlockSize;
  986.     }
  987.     }
  988.  
  989.     UNLOCK_MONITOR;
  990. }
  991.  
  992.  
  993. /*
  994.  * ----------------------------------------------------------------------------
  995.  *
  996.  *    Functions to perform some action on a file.  This includes write-back
  997.  *    and invalidation.
  998.  *
  999.  * ----------------------------------------------------------------------------
  1000.  */
  1001.  
  1002. /*
  1003.  * ----------------------------------------------------------------------------
  1004.  *
  1005.  * Fscache_FileInvalidate --
  1006.  *
  1007.  *     This function removes from the cache all blocks for the file 
  1008.  *    identified by *filePtr in the range firstBlock to lastBlock.
  1009.  *
  1010.  * Results:
  1011.  *    None.
  1012.  *
  1013.  * Side effects:
  1014.  *    All blocks in the cache for the file are removed.
  1015.  *
  1016.  * ----------------------------------------------------------------------------
  1017.  *
  1018.  */
  1019. ENTRY void
  1020. Fscache_FileInvalidate(cacheInfoPtr, firstBlock, lastBlock)
  1021.     Fscache_FileInfo *cacheInfoPtr;    /* Cache state of file to invalidate. */
  1022.     int        firstBlock;    /* First block to invalidate. Starts at zero. */
  1023.     int        lastBlock;    /* Last block to invalidate.  FSCACHE_LAST_BLOCK
  1024.                  * can be used if the caller doesn't know
  1025.                  * the exact last block of the file. */
  1026. {
  1027.     LOCK_MONITOR;
  1028.  
  1029.     if (lastBlock == FSCACHE_LAST_BLOCK) {
  1030.     if (cacheInfoPtr->attr.lastByte > 0) {
  1031.         lastBlock = cacheInfoPtr->attr.lastByte / FS_BLOCK_SIZE;
  1032.     } else {
  1033.         lastBlock = 0;
  1034.     }
  1035.     }
  1036.     CacheFileInvalidate(cacheInfoPtr, firstBlock, lastBlock);
  1037.  
  1038.     UNLOCK_MONITOR;
  1039. }
  1040.  
  1041.  
  1042.  
  1043. /*
  1044.  * ----------------------------------------------------------------------------
  1045.  *
  1046.  * CacheFileInvalidate --
  1047.  *
  1048.  *     This function removes from the cache all blocks for the given file 
  1049.  *    identified in the range firstBlock to lastBlock.  If any blocks are 
  1050.  *    being written to disk, it will block until they have finished being
  1051.  *    written.
  1052.  *
  1053.  * Results:
  1054.  *    None.
  1055.  *
  1056.  * Side effects:
  1057.  *    All blocks in the cache for the file are removed.
  1058.  *
  1059.  * ----------------------------------------------------------------------------
  1060.  */
  1061. INTERNAL static void
  1062. CacheFileInvalidate(cacheInfoPtr, firstBlock, lastBlock)
  1063.     Fscache_FileInfo    *cacheInfoPtr;    /* File to invalidate. */
  1064.     int            firstBlock;    /* First block to invalidate. */
  1065.     int            lastBlock;    /* Last block to invalidate. */
  1066. {
  1067.     register Hash_Entry         *hashEntryPtr;
  1068.     register Fscache_Block    *blockPtr;
  1069.     BlockHashKey         blockHashKey;
  1070.     int                 i;
  1071.  
  1072.  
  1073.     if (cacheInfoPtr->blocksInCache > 0) {
  1074.     SET_BLOCK_HASH_KEY(blockHashKey, cacheInfoPtr, 0);
  1075.  
  1076.     for (i = firstBlock; i <= lastBlock; i++) {
  1077.         hashEntryPtr = GetUnlockedBlock(&blockHashKey, i);
  1078.         if (hashEntryPtr == (Hash_Entry *) NIL) {
  1079.         continue;
  1080.         }
  1081.         blockPtr = (Fscache_Block *) Hash_GetValue(hashEntryPtr);
  1082.         if (blockPtr->fileNum != cacheInfoPtr->hdrPtr->fileID.minor) {
  1083.         panic( "CacheFileInvalidate, hashing error\n");
  1084.         continue;
  1085.         }
  1086.  
  1087.         /*
  1088.          * Remove it from the hash table.
  1089.          */
  1090.         cacheInfoPtr->blocksInCache--;
  1091.         List_Remove(&blockPtr->fileLinks);
  1092.         Hash_Delete(blockHashTable, hashEntryPtr);
  1093.  
  1094.         /*
  1095.          * Invalidate the block, including removing it from dirty list
  1096.          * if necessary.
  1097.          */
  1098.         if (blockPtr->flags & FSCACHE_BLOCK_DIRTY) {
  1099.         cacheInfoPtr->numDirtyBlocks--;
  1100.         DeleteBlockFromDirtyList(blockPtr);
  1101.         numAvailBlocks++;
  1102.         }
  1103.         List_Remove(&blockPtr->useLinks);
  1104.         PutOnFreeList(blockPtr);
  1105.     }
  1106.     }
  1107.     if (cacheInfoPtr->blocksInCache == 0) {
  1108.     cacheInfoPtr->flags &=
  1109.             ~(FSCACHE_SERVER_DOWN | FSCACHE_NO_DISK_SPACE |
  1110.               FSCACHE_DOMAIN_DOWN | FSCACHE_GENERIC_ERROR);
  1111.     }
  1112. }
  1113.  
  1114.  
  1115. /*
  1116.  * ----------------------------------------------------------------------------
  1117.  *
  1118.  * DeleteBlockFromDirtyList --
  1119.  *
  1120.  *     Delete the given block from the dirty list.  This is done when
  1121.  *    the file is being deleted.
  1122.  *
  1123.  * Results:
  1124.  *    None.
  1125.  *
  1126.  * Side effects:
  1127.  *    If this is the last dirty block of a file then the file is
  1128.  *    removed from the file dirty list.  Also, if someone was waiting
  1129.  *    on the block the global writeBackComplete is notified.
  1130.  *
  1131.  * ----------------------------------------------------------------------------
  1132.  */
  1133. INTERNAL static void
  1134. DeleteBlockFromDirtyList(blockPtr)
  1135.     register    Fscache_Block    *blockPtr;
  1136. {
  1137.     register    Fscache_FileInfo    *cacheInfoPtr;
  1138.  
  1139.     cacheInfoPtr = blockPtr->cacheInfoPtr;
  1140.     List_Remove(&blockPtr->dirtyLinks);
  1141.     if ((cacheInfoPtr->flags & FSCACHE_FILE_ON_DIRTY_LIST) &&
  1142.         (cacheInfoPtr->numDirtyBlocks == 0) &&
  1143.     !(cacheInfoPtr->flags & FSCACHE_FILE_DESC_DIRTY)) {
  1144.     /*
  1145.      * No more dirty blocks for this file.  Remove the file from the dirty
  1146.      * list and wakeup anyone waiting for the file's list to become
  1147.      * empty.
  1148.      */
  1149.     List_Remove((List_Links *)cacheInfoPtr);
  1150.     cacheInfoPtr->flags &= ~(FSCACHE_FILE_ON_DIRTY_LIST|FSCACHE_FILE_FSYNC|FSCACHE_FILE_BEING_CLEANED);
  1151.     Sync_Broadcast(&cacheInfoPtr->noDirtyBlocks);
  1152.     }
  1153.  
  1154. }
  1155.  
  1156. /*
  1157.  *----------------------------------------------------------------------
  1158.  *
  1159.  * StartFileSync --
  1160.  *
  1161.  *    Start the process of syncing from the cache to the backend.
  1162.  *
  1163.  * Results:
  1164.  *    None.
  1165.  *
  1166.  * Side effects:
  1167.  *    Backend may be started. File may be moved up the dirty list.
  1168.  *
  1169.  *----------------------------------------------------------------------
  1170.  */
  1171. static void
  1172. StartFileSync(cacheInfoPtr)
  1173.     Fscache_FileInfo    *cacheInfoPtr;
  1174. {
  1175.     register List_Links    *linkPtr;
  1176.     List_Links    *dirtyList, *place;
  1177.  
  1178.     dirtyList = &cacheInfoPtr->backendPtr->dirtyListHdr;
  1179.  
  1180.     /*
  1181.      * If the file has not already been synced, move the file up the
  1182.      * dirty list to the front or right behide the last synced file.
  1183.      */
  1184.     if (!(cacheInfoPtr->flags & FSCACHE_FILE_FSYNC)) {
  1185.     /*
  1186.      * Move down the list until we reach the file or the first non
  1187.      * synced file. 
  1188.      */
  1189.     place = (List_Links *) cacheInfoPtr;
  1190.     LIST_FORALL(dirtyList, linkPtr) {
  1191.         if ((linkPtr == (List_Links *) cacheInfoPtr) ||
  1192.         !(((Fscache_FileInfo *) linkPtr)->flags & FSCACHE_FILE_FSYNC)) {
  1193.         place = linkPtr;
  1194.         break;
  1195.         }
  1196.     }
  1197.     cacheInfoPtr->flags |= FSCACHE_FILE_FSYNC;
  1198.     if (place != (List_Links *) cacheInfoPtr) {
  1199.         List_Move((List_Links *)cacheInfoPtr, LIST_BEFORE(place));
  1200.     }
  1201.     }
  1202.     StartBackendWriteback(cacheInfoPtr->backendPtr);
  1203. }
  1204.  
  1205.  
  1206. /*
  1207.  * ----------------------------------------------------------------------------
  1208.  *
  1209.  * Fscache_FileWriteBack --
  1210.  *
  1211.  *     This function forces all blocks for the file identified by 
  1212.  *    *hdrPtr in the range firstBlock to lastBlock to disk (or 
  1213.  *    the server).
  1214.  *
  1215.  * Results:
  1216.  *    None.
  1217.  *
  1218.  * Side effects:
  1219.  *    All dirty blocks in the cache for the file are written out.
  1220.  *
  1221.  * ----------------------------------------------------------------------------
  1222.  *
  1223.  */
  1224. ENTRY ReturnStatus
  1225. Fscache_FileWriteBack(cacheInfoPtr, firstBlock, lastBlock, flags,
  1226.     blocksSkippedPtr)
  1227.     register Fscache_FileInfo *cacheInfoPtr;    /* State to force out. */
  1228.     int        firstBlock;    /* First block to write back. */
  1229.     int        lastBlock;    /* Last block to write back. */
  1230.     int        flags;        /* FSCACHE_FILE_WB_WAIT | FSCACHE_WRITE_BACK_INDIRECT |
  1231.                  * FSCACHE_WRITE_BACK_AND_INVALIDATE. 
  1232.                  * FSCACHE_WB_MIGRATION. */
  1233.     int        *blocksSkippedPtr; /* The number of blocks skipped
  1234.                       because they were locked. */
  1235. {
  1236.     register Hash_Entry         *hashEntryPtr;
  1237.     register Fscache_Block    *blockPtr;
  1238.     BlockHashKey         blockHashKey;
  1239.     int                 i;
  1240.     ReturnStatus         status;
  1241.     Boolean             fsyncFile;
  1242.     enum  {ENTIRE_FILE, SINGLE_BLOCK, MULTI_BLOCK_RANGE} rangeType;
  1243.  
  1244.     LOCK_MONITOR;
  1245.  
  1246.     *blocksSkippedPtr = 0;
  1247.  
  1248.     fsyncFile = FALSE;
  1249.     /* 
  1250.      * First classify the type of writeback being requests as either involving
  1251.      * a single file block, the entire file, or a multiple-block range.
  1252.      */
  1253.     if ((firstBlock == lastBlock) && (firstBlock >= 0)) {
  1254.     rangeType = SINGLE_BLOCK;
  1255.     } else if ((firstBlock == 0) && 
  1256.             ((lastBlock == FSCACHE_LAST_BLOCK) || 
  1257.             (lastBlock == cacheInfoPtr->attr.lastByte / FS_BLOCK_SIZE))) {
  1258.     rangeType = ENTIRE_FILE;
  1259.     } else {
  1260.     rangeType = MULTI_BLOCK_RANGE;
  1261.     }
  1262.  
  1263.     /*
  1264.      * Clear out the host down and no disk space flags so we can retry
  1265.      * for this file.
  1266.      */
  1267.     cacheInfoPtr->flags &= 
  1268.             ~(FSCACHE_SERVER_DOWN | FSCACHE_NO_DISK_SPACE | 
  1269.               FSCACHE_DOMAIN_DOWN | FSCACHE_GENERIC_ERROR);
  1270.  
  1271.     if ((cacheInfoPtr->blocksInCache == 0) ||
  1272.     (flags & FSCACHE_WRITE_BACK_DESC_ONLY)) {
  1273.     /*
  1274.      * If file is on dirty list and has no blocks then it 
  1275.      * descriptor is dirty.  Force the descriptor out 
  1276.      * waiting if the user specified to.
  1277.      */
  1278.     if ((cacheInfoPtr->flags & FSCACHE_FILE_ON_DIRTY_LIST) &&
  1279.         (flags & FSCACHE_WRITE_BACK_DESC_ONLY)) {
  1280.         StartFileSync(cacheInfoPtr);
  1281.         if (flags & FSCACHE_FILE_WB_WAIT) {
  1282.         while (cacheInfoPtr->flags & FSCACHE_FILE_ON_DIRTY_LIST) {
  1283.             (void) Sync_Wait(&cacheInfoPtr->noDirtyBlocks, FALSE);
  1284.         }
  1285.         }
  1286.     }
  1287.     UNLOCK_MONITOR;
  1288.     return(SUCCESS);
  1289.     }
  1290.  
  1291.     SET_BLOCK_HASH_KEY(blockHashKey, cacheInfoPtr, 0);
  1292.  
  1293.     if (lastBlock == FSCACHE_LAST_BLOCK) {
  1294.     if (cacheInfoPtr->attr.lastByte > 0) {
  1295.         lastBlock = cacheInfoPtr->attr.lastByte / FS_BLOCK_SIZE;
  1296.     } else {
  1297.         lastBlock = 0;
  1298.     }
  1299.     }
  1300.     blockPtr = (Fscache_Block *) NIL;
  1301.     for (i = firstBlock; i <= lastBlock; i++) {
  1302.     /*
  1303.      * See if block is in the hash table.
  1304.      */
  1305.  
  1306.     blockHashKey.blockNumber = i;
  1307. again:
  1308.     hashEntryPtr = Hash_LookOnly(blockHashTable, (Address) &blockHashKey);
  1309.     if (hashEntryPtr == (Hash_Entry *) NIL) {
  1310.         continue;
  1311.     }
  1312.  
  1313.     blockPtr = (Fscache_Block *) Hash_GetValue(hashEntryPtr);
  1314.  
  1315.     if (blockPtr->fileNum != cacheInfoPtr->hdrPtr->fileID.minor) {
  1316.         panic( "Fscache_FileWriteBack, hashing error\n");
  1317.         UNLOCK_MONITOR;
  1318.         return(FAILURE);
  1319.     }
  1320.  
  1321.     if (flags & (FSCACHE_WRITE_BACK_AND_INVALIDATE | FSCACHE_FILE_WB_WAIT)) {
  1322.         /*
  1323.          * Wait for the block to become unlocked.  If have to wait then
  1324.          * must start over because the block could have been freed while
  1325.          * we were sleeping.
  1326.          */
  1327.         if (blockPtr->refCount > 0) {
  1328.         (void) Sync_Wait(&blockPtr->ioDone, FALSE);
  1329.         if (sys_ShuttingDown) {
  1330.             UNLOCK_MONITOR;
  1331.             return(SUCCESS);
  1332.         }
  1333.         goto again;
  1334.         }
  1335.     } else if (blockPtr->refCount > 0) {
  1336.         /* 
  1337.          * If the block is locked and we are not invalidating it, 
  1338.          * skip the block because it might be being modified.
  1339.          */
  1340.         (*blocksSkippedPtr)++;
  1341.         continue;
  1342.     }
  1343.  
  1344.     if (blockPtr->flags & (FSCACHE_BLOCK_DIRTY|FSCACHE_BLOCK_BEING_WRITTEN)) {
  1345.         fsyncFile = TRUE;
  1346.     }
  1347.     if (flags & FSCACHE_WRITE_BACK_AND_INVALIDATE) {
  1348.         if (blockPtr->flags & 
  1349.             (FSCACHE_BLOCK_DIRTY|FSCACHE_BLOCK_BEING_WRITTEN)) {
  1350.         /*
  1351.          * Mark the block to be release by the block cleaner. We
  1352.          * delete it from the hash table and remove it from the LRU
  1353.          * list for the block cleaner. 
  1354.          */
  1355.         if (!(blockPtr->flags & FSCACHE_BLOCK_DELETED)) { 
  1356.             blockPtr->flags |= FSCACHE_BLOCK_DELETED;
  1357.             Hash_Delete(blockHashTable, hashEntryPtr);
  1358.             List_Remove( &blockPtr->useLinks);
  1359.         }
  1360.         } else {
  1361.         /*
  1362.          * The block is clean and not being written, We remove it
  1363.          * from cache.
  1364.          */
  1365.         cacheInfoPtr->blocksInCache--;
  1366.         List_Remove(&blockPtr->fileLinks);
  1367.         Hash_Delete(blockHashTable, hashEntryPtr);
  1368.         List_Remove(&blockPtr->useLinks);
  1369.         PutOnFreeList(blockPtr);
  1370.         }
  1371.     } 
  1372.     }
  1373.     if ((cacheInfoPtr->flags & FSCACHE_FILE_ON_DIRTY_LIST) && fsyncFile) {
  1374.     StartFileSync(cacheInfoPtr);
  1375.     }
  1376.  
  1377.     /*
  1378.      * Wait until all blocks are written back.
  1379.      */
  1380.     if (flags & FSCACHE_FILE_WB_WAIT) {
  1381.      if ((rangeType == SINGLE_BLOCK) && fsyncFile) {
  1382.         while ((blockPtr->flags & 
  1383.         (FSCACHE_BLOCK_DIRTY|FSCACHE_BLOCK_BEING_WRITTEN)) && 
  1384.            !(cacheInfoPtr->flags & 
  1385.             (FSCACHE_SERVER_DOWN | FSCACHE_NO_DISK_SPACE |
  1386.              FSCACHE_DOMAIN_DOWN | FSCACHE_GENERIC_ERROR)) &&
  1387.            !sys_ShuttingDown) {
  1388.         (void) Sync_Wait(&blockPtr->ioDone, FALSE);
  1389.         }
  1390.      } else { 
  1391.         while ((cacheInfoPtr->flags & 
  1392.             (FSCACHE_FILE_ON_DIRTY_LIST|FSCACHE_FILE_BEING_WRITTEN)) && 
  1393.            !(cacheInfoPtr->flags & 
  1394.             (FSCACHE_SERVER_DOWN | FSCACHE_NO_DISK_SPACE |
  1395.              FSCACHE_DOMAIN_DOWN | FSCACHE_GENERIC_ERROR)) &&
  1396.            !sys_ShuttingDown) {
  1397.         (void) Sync_Wait(&cacheInfoPtr->noDirtyBlocks, FALSE);
  1398.         }
  1399.     }
  1400.     }
  1401.  
  1402.     switch (cacheInfoPtr->flags&(FSCACHE_SERVER_DOWN|FSCACHE_NO_DISK_SPACE|
  1403.                  FSCACHE_DOMAIN_DOWN|FSCACHE_GENERIC_ERROR)) {
  1404.     case FSCACHE_SERVER_DOWN:
  1405.         status = RPC_TIMEOUT;
  1406.         break;
  1407.     case FSCACHE_NO_DISK_SPACE:
  1408.         status = FS_NO_DISK_SPACE;
  1409.         break;
  1410.     case FSCACHE_DOMAIN_DOWN:
  1411.         status = FS_DOMAIN_UNAVAILABLE;
  1412.         break;
  1413.     case FSCACHE_GENERIC_ERROR:
  1414.         status = FS_INVALID_ARG;
  1415.         break;
  1416.     default:
  1417.         status = SUCCESS;
  1418.         break;
  1419.     }
  1420.  
  1421.     UNLOCK_MONITOR;
  1422.  
  1423.     return(status);
  1424. }
  1425.  
  1426.  
  1427. /*
  1428.  * ----------------------------------------------------------------------------
  1429.  *
  1430.  * Fscache_BlocksUnneeded --
  1431.  *
  1432.  *    This function moves the blocks that span the given range of bytes to
  1433.  *    the front of the LRU list and marks them as not referenced.  This
  1434.  *    function is called by virtual memory after it has read in an object
  1435.  *    file block that it will cache in a sticky segment.  If we are the
  1436.  *    file server and are being called for an object file, then don't do 
  1437.  *    anything because other clients might need the blocks.
  1438.  *
  1439.  * Results:
  1440.  *    None.
  1441.  *
  1442.  * Side effects:
  1443.  *    None here, see FscacheBlocksUnneeded.
  1444.  *    All blocks that span the given range of bytes are moved to the front of
  1445.  *    the LRU list and marked as not-referenced.
  1446.  *
  1447.  * ----------------------------------------------------------------------------
  1448.  *
  1449.  */
  1450. void
  1451. Fscache_BlocksUnneeded(streamPtr, offset, numBytes, objectFile)
  1452.     register Fs_Stream    *streamPtr;    /* File for which blocks are unneeded.*/
  1453.     int            offset;        /* First byte which is unneeded. */
  1454.     int            numBytes;    /* Number of bytes that are unneeded. */
  1455.     Boolean        objectFile;    /* TRUE if this is for an object 
  1456.                      * file.*/
  1457. {
  1458.     register Fscache_FileInfo *cacheInfoPtr;
  1459.  
  1460.     switch (streamPtr->ioHandlePtr->fileID.type) {
  1461.     case FSIO_LCL_FILE_STREAM: {
  1462.         register Fsio_FileIOHandle *localHandlePtr;
  1463.         if (objectFile) {
  1464.         /*
  1465.          * Keep the blocks cached for remote clients.
  1466.          */
  1467.         return;
  1468.         }
  1469.         localHandlePtr = (Fsio_FileIOHandle *)streamPtr->ioHandlePtr;
  1470.         cacheInfoPtr = &localHandlePtr->cacheInfo;
  1471.         break;
  1472.     }
  1473.     case FSIO_RMT_FILE_STREAM: {
  1474.         register Fsrmt_FileIOHandle *rmtHandlePtr;
  1475.         rmtHandlePtr = (Fsrmt_FileIOHandle *)streamPtr->ioHandlePtr;
  1476.         cacheInfoPtr = &rmtHandlePtr->cacheInfo;
  1477.         break;
  1478.     }
  1479.     default:
  1480.         panic( "Fscache_BlocksUnneeded, bad stream type %d\n",
  1481.         streamPtr->ioHandlePtr->fileID.type);
  1482.         return;
  1483.     }
  1484.     FscacheBlocksUnneeded(cacheInfoPtr, offset, numBytes);
  1485. }
  1486.  
  1487.  
  1488. /*
  1489.  * ----------------------------------------------------------------------------
  1490.  *
  1491.  * FscacheBlocksUnneeded --
  1492.  *
  1493.  *    This function moves the blocks that span the given range of bytes to
  1494.  *    the front of the LRU list and marks them as not referenced.
  1495.  *
  1496.  * Results:
  1497.  *    None.
  1498.  *
  1499.  * Side effects:
  1500.  *    All blocks that span the given range of bytes are moved to the front of
  1501.  *    the LRU list and marked as not-referenced.
  1502.  *
  1503.  * ----------------------------------------------------------------------------
  1504.  *
  1505.  */
  1506. ENTRY void
  1507. FscacheBlocksUnneeded(cacheInfoPtr, offset, numBytes)
  1508.     register Fscache_FileInfo *cacheInfoPtr;    /* Cache state. */
  1509.     int            offset;        /* First byte which is unneeded. */
  1510.     int            numBytes;    /* Number of bytes that are unneeded. */
  1511. {
  1512.     register Hash_Entry        *hashEntryPtr;
  1513.     register Fscache_Block        *blockPtr;
  1514.     BlockHashKey             blockHashKey;
  1515.     int                     i;
  1516.     int                firstBlock;
  1517.     int                lastBlock;
  1518.  
  1519.     LOCK_MONITOR;
  1520.  
  1521.     if (cacheInfoPtr->blocksInCache == 0) {
  1522.     UNLOCK_MONITOR;
  1523.     return;
  1524.     }
  1525.  
  1526.     firstBlock = offset / FS_BLOCK_SIZE;
  1527.     lastBlock = (offset + numBytes - 1) / FS_BLOCK_SIZE;
  1528.     SET_BLOCK_HASH_KEY(blockHashKey, cacheInfoPtr, 0);
  1529.  
  1530.     for (i = firstBlock; i <= lastBlock; i++) {
  1531.     /*
  1532.      * See if block is in the hash table.
  1533.      */
  1534.     blockHashKey.blockNumber = i;
  1535.     hashEntryPtr = Hash_LookOnly(blockHashTable, (Address) &blockHashKey);
  1536.     if (hashEntryPtr == (Hash_Entry *) NIL) {
  1537.         continue;
  1538.     }
  1539.  
  1540.     blockPtr = (Fscache_Block *) Hash_GetValue(hashEntryPtr);
  1541.  
  1542.     if (blockPtr->fileNum != cacheInfoPtr->hdrPtr->fileID.minor) {
  1543.         panic( "CacheBlocksUnneeded, hashing error\n");
  1544.         continue;
  1545.     }
  1546.  
  1547.     if (blockPtr->refCount > 0) {
  1548.         /*
  1549.          * The block is locked.  This means someone is doing something with
  1550.          * it so we just skip it.
  1551.          */
  1552.         continue;
  1553.     }
  1554.     if (blockPtr->flags & 
  1555.             (FSCACHE_BLOCK_DIRTY|FSCACHE_BLOCK_BEING_WRITTEN)) {
  1556.         /*
  1557.          * Block is being cleaned.  Set the flag so that it will be 
  1558.          * moved to the front after it has been cleaned.
  1559.          */
  1560.         blockPtr->flags |= FSCACHE_MOVE_TO_FRONT;
  1561.     } else {
  1562.         /*
  1563.          * Move the block to the front of the LRU list.
  1564.          */
  1565.         List_Move(&blockPtr->useLinks, LIST_ATFRONT(lruList));
  1566.     }
  1567.     fs_Stats.blockCache.blocksPitched++;
  1568.     /*
  1569.      * Set time referenced to zero so this block will be taken as soon
  1570.      * as needed.
  1571.      */
  1572.     blockPtr->timeReferenced = 0;
  1573.     }
  1574.  
  1575.     UNLOCK_MONITOR;
  1576. }
  1577.  
  1578.  
  1579. /*
  1580.  * ----------------------------------------------------------------------------
  1581.  *
  1582.  *    Functions to perform an action on the entire cache. 
  1583.  *
  1584.  * ----------------------------------------------------------------------------
  1585.  */
  1586.  
  1587. /*
  1588.  * ----------------------------------------------------------------------------
  1589.  *
  1590.  * Fscache_WriteBack --
  1591.  *
  1592.  *    Force all dirty blocks in the cache that were dirtied before
  1593.  *    writeBackTime to disk (or the server).  
  1594.  *
  1595.  * Results:
  1596.  *    None.
  1597.  *
  1598.  * Side effects:
  1599.  *    All dirty blocks in the cache are written out.
  1600.  *
  1601.  * ----------------------------------------------------------------------------
  1602.  *
  1603.  */
  1604. ENTRY void
  1605. Fscache_WriteBack(writeBackTime, blocksSkippedPtr, writeBackAll)
  1606.     unsigned int writeBackTime;       /* Write back all blocks that were 
  1607.                       dirtied before this time. */
  1608.     int        *blocksSkippedPtr; /* The number of blocks skipped
  1609.                       because they were locked. */
  1610.     Boolean    writeBackAll;       /* Write back all files. */
  1611. {
  1612.     LOCK_MONITOR;
  1613.     CacheWriteBack(writeBackTime, blocksSkippedPtr, writeBackAll);
  1614.  
  1615.     UNLOCK_MONITOR;
  1616. }
  1617.  
  1618.  
  1619. /*
  1620.  * ----------------------------------------------------------------------------
  1621.  *
  1622.  * Fscache_Empty --
  1623.  *
  1624.  *    Write back and invalidate all unlocked blocks from the cache.
  1625.  *
  1626.  * Results:
  1627.  *    The number of locked blocks is returned in the argument.
  1628.  *
  1629.  * Side effects:
  1630.  *    All unlocked blocks are written back, if necessary, and invalidated.
  1631.  *
  1632.  * ----------------------------------------------------------------------------
  1633.  *
  1634.  */
  1635. ENTRY void
  1636. Fscache_Empty(numLockedBlocksPtr)
  1637.     int *numLockedBlocksPtr;
  1638. {
  1639.     int             blocksSkipped;
  1640.     register    Fscache_Block    *blockPtr;
  1641.     register    List_Links    *nextPtr, *listPtr;
  1642.  
  1643.     LOCK_MONITOR;
  1644.  
  1645.     *numLockedBlocksPtr = 0;
  1646.     CacheWriteBack(-1, &blocksSkipped, TRUE);
  1647.     listPtr = lruList;
  1648.     nextPtr = List_First(listPtr);
  1649.     while (!List_IsAtEnd(listPtr, nextPtr)) {
  1650.     blockPtr = USE_LINKS_TO_BLOCK(nextPtr);
  1651.     nextPtr = List_Next(nextPtr);
  1652.     if (blockPtr->refCount > 0 || 
  1653.         (blockPtr->flags & 
  1654.         (FSCACHE_BLOCK_DIRTY|FSCACHE_BLOCK_BEING_WRITTEN))) {
  1655.         /* 
  1656.          * Skip locked or dirty blocks.
  1657.          */
  1658.         (*numLockedBlocksPtr)++;
  1659.     } else {
  1660.         List_Remove(&blockPtr->useLinks);
  1661.         DeleteBlock(blockPtr);
  1662.         PutOnFreeList(blockPtr);
  1663.     }
  1664.     }
  1665. #ifdef SOSP91
  1666.     /* XXX Darn - I'll miss blocks quicked out of the cache due to sync. */
  1667. #endif SOSP91
  1668.  
  1669.     UNLOCK_MONITOR;
  1670. }
  1671.  
  1672.  
  1673. /*
  1674.  * ----------------------------------------------------------------------------
  1675.  *
  1676.  * CacheWriteBack --
  1677.  *
  1678.  *    Force all dirty blocks in the cache that were dirtied before
  1679.  *    writeBackTime to disk (or the server).  If writeBackTime equals 
  1680.  *    -1 then all blocks are written back.
  1681.  *
  1682.  * Results:
  1683.  *    None.
  1684.  *
  1685.  * Side effects:
  1686.  *    All dirty blocks in the cache are written out.
  1687.  *
  1688.  * ----------------------------------------------------------------------------
  1689.  *
  1690.  */
  1691. static INTERNAL void
  1692. CacheWriteBack(writeBackTime, blocksSkippedPtr, writeTmpFiles)
  1693.     unsigned int writeBackTime;       /* Write back all blocks that were 
  1694.                       dirtied before this time. */
  1695.     int        *blocksSkippedPtr; /* The number of blocks skipped
  1696.                       because they were locked. */
  1697.     Boolean    writeTmpFiles;       /* TRUE => write-back tmp files even though
  1698.                     *         they are marked as not being
  1699.                     *         written back. */
  1700. {
  1701.     register Fscache_FileInfo    *cacheInfoPtr;
  1702.     int                currentTime;
  1703.     Fscache_Backend        *backendPtr;
  1704.  
  1705.     currentTime = Fsutil_TimeInSeconds();
  1706.  
  1707.     *blocksSkippedPtr = 0;
  1708.     /*
  1709.      * Look thru all the cache backend`s dirty list for files to 
  1710.      * writeback.
  1711.      */
  1712.     filewriteBackTime = writeBackTime;
  1713.     LIST_FORALL(backendList, (List_Links *) backendPtr) {
  1714.     LIST_FORALL(&backendPtr->dirtyListHdr, (List_Links *) cacheInfoPtr) { 
  1715.         /*
  1716.          * Check to see if we should start a block cleaner for this
  1717.          * backend.
  1718.          */
  1719.         if (cacheInfoPtr->flags & (FSCACHE_SERVER_DOWN|FSCACHE_FILE_GONE)) {
  1720.         /*
  1721.          * Don't bother to write-back files for which the server is
  1722.          * down.  These will be written back during recovery.
  1723.          */
  1724.         continue;
  1725.         }
  1726.         if (cacheInfoPtr->flags &
  1727.             (FSCACHE_NO_DISK_SPACE | FSCACHE_DOMAIN_DOWN |
  1728.              FSCACHE_GENERIC_ERROR)) {
  1729.         /*
  1730.          * Retry for these types of errors.
  1731.          */
  1732.         if (cacheInfoPtr->lastTimeTried < currentTime) {
  1733.             cacheInfoPtr->flags &=
  1734.             ~(FSCACHE_NO_DISK_SPACE | FSCACHE_DOMAIN_DOWN |
  1735.                   FSCACHE_GENERIC_ERROR);
  1736.             StartBackendWriteback(cacheInfoPtr->backendPtr);
  1737.             break;
  1738.         } else {
  1739.             continue;
  1740.         }
  1741.         }
  1742.         if ((numAvailBlocks < minNumAvailBlocks + FSCACHE_MIN_BLOCKS) ||
  1743.         (cacheInfoPtr->oldestDirtyBlockTime < writeBackTime) ||
  1744.         (cacheInfoPtr->flags & FSCACHE_FILE_FSYNC)) {
  1745.         StartBackendWriteback(cacheInfoPtr->backendPtr);
  1746.         break;
  1747.         }
  1748.     }
  1749.     }
  1750.     /*
  1751.      * Wait for all block cleaners to go idea before returning.
  1752.      */
  1753.     while ((numBackendsActive > 0) && !sys_ShuttingDown) {
  1754.     (void) Sync_Wait(&writeBackComplete, FALSE);
  1755.     }
  1756. }
  1757.  
  1758.  
  1759. /*
  1760.  * ----------------------------------------------------------------------------
  1761.  *
  1762.  *    Functions to create, destroy and allocate cache blocks.  These 
  1763.  *    functions are used to provide the variable sized cache and the LRU
  1764.  *    algorithm for managing cache blocks.
  1765.  *
  1766.  * ----------------------------------------------------------------------------
  1767.  */
  1768.  
  1769. /*
  1770.  * ----------------------------------------------------------------------------
  1771.  *
  1772.  * Fscache_SetMinSize --
  1773.  *
  1774.  *     Set the minimum size of the block cache.  This will entail mapping
  1775.  *    enough blocks so that the number of physical pages in use is greater
  1776.  *    than or equal to the minimum number.
  1777.  *
  1778.  * Results:
  1779.  *    None.
  1780.  *
  1781.  * Side effects:
  1782.  *    More blocks get memory put behind them.
  1783.  *
  1784.  * ----------------------------------------------------------------------------
  1785.  */
  1786. ENTRY void
  1787. Fscache_SetMinSize(minBlocks)
  1788.     int    minBlocks;    /* The minimum number of blocks in the cache. */
  1789. {
  1790.     LOCK_MONITOR;
  1791.  
  1792.  
  1793.  
  1794.     if (minBlocks > fs_Stats.blockCache.maxNumBlocks) {
  1795.     minBlocks = fs_Stats.blockCache.maxNumBlocks;
  1796.     printf( "Fscache_SetMinSize: Only raising min cache size to %d blocks\n", 
  1797.                 minBlocks);
  1798.     }
  1799.     fs_Stats.blockCache.minCacheBlocks = minBlocks;
  1800.     if (fs_Stats.blockCache.minCacheBlocks <= 
  1801.                     fs_Stats.blockCache.numCacheBlocks) {
  1802.     UNLOCK_MONITOR;
  1803.     return;
  1804.     }
  1805.  
  1806.     /*
  1807.      * Give enough blocks memory so that the minimum cache size requirement
  1808.      * is met.
  1809.      */
  1810.     while (fs_Stats.blockCache.numCacheBlocks < 
  1811.                     fs_Stats.blockCache.minCacheBlocks) {
  1812.     if (!CreateBlock(FALSE, (Fscache_Block **) NIL)) {
  1813.         printf("Fscache_SetMinSize: lowered min cache size to %d blocks\n",
  1814.                fs_Stats.blockCache.numCacheBlocks);
  1815.         fs_Stats.blockCache.minCacheBlocks = 
  1816.                     fs_Stats.blockCache.numCacheBlocks;
  1817.     }
  1818.     }
  1819.  
  1820.     UNLOCK_MONITOR;
  1821. }
  1822.  
  1823.  
  1824. /*
  1825.  * ----------------------------------------------------------------------------
  1826.  *
  1827.  * Fscache_SetMaxSize --
  1828.  *
  1829.  *     Set the maximum size of the block cache.  This entails freeing
  1830.  *    enough main memory pages so that the number of cache pages is
  1831.  *    less than the maximum allowed.
  1832.  *
  1833.  * Results:
  1834.  *    None.
  1835.  *
  1836.  * Side effects:
  1837.  *    Cache blocks have memory removed from behind them and are moved to
  1838.  *    the unmapped list. 
  1839.  *
  1840.  * ----------------------------------------------------------------------------
  1841.  */
  1842. ENTRY void
  1843. Fscache_SetMaxSize(maxBlocks)
  1844.     int    maxBlocks;    /* The minimum number of pages in the cache. */
  1845. {
  1846.     Boolean            giveUp;
  1847.     int                pageNum;
  1848.  
  1849.     LOCK_MONITOR;
  1850.  
  1851.     if (maxBlocks > fs_Stats.blockCache.maxNumBlocks) {
  1852.     maxBlocks = fs_Stats.blockCache.maxNumBlocks;
  1853.     printf("Fscache_SetMaxSize: Only raising max cache size to %d blocks\n",
  1854.         maxBlocks);
  1855.     }
  1856.  
  1857.     fs_Stats.blockCache.maxCacheBlocks = maxBlocks;
  1858.     if (fs_Stats.blockCache.maxCacheBlocks >= 
  1859.                     fs_Stats.blockCache.numCacheBlocks) {
  1860.     UNLOCK_MONITOR;
  1861.     return;
  1862.     }
  1863.     
  1864.     /*
  1865.      * Free enough pages to get down to maximum size.
  1866.      */
  1867.     giveUp = FALSE;
  1868.     while (fs_Stats.blockCache.numCacheBlocks > 
  1869.                 fs_Stats.blockCache.maxCacheBlocks && !giveUp) {
  1870.     giveUp = !DestroyBlock(FALSE, &pageNum);
  1871.     }
  1872.  
  1873.  
  1874.     UNLOCK_MONITOR;
  1875. }
  1876.  
  1877.  
  1878. /*
  1879.  * ----------------------------------------------------------------------------
  1880.  *
  1881.  * Fscache_GetPageFromFS --
  1882.  *
  1883.  *     Compare LRU time of the caller to time of block in LRU list and
  1884.  *    if caller has newer pages unmap a block and return a page.
  1885.  *
  1886.  * Results:
  1887.  *    Physical page number if unmap a block.
  1888.  *
  1889.  * Side effects:
  1890.  *    Blocks may be unmapped.
  1891.  *
  1892.  * ----------------------------------------------------------------------------
  1893.  */
  1894. ENTRY void
  1895. Fscache_GetPageFromFS(timeLastAccessed, pageNumPtr)
  1896.     time_t    timeLastAccessed;
  1897.     int        *pageNumPtr;
  1898. {
  1899.     register    Fscache_Block    *blockPtr;
  1900.  
  1901.     LOCK_MONITOR;
  1902.  
  1903.     fs_Stats.blockCache.vmRequests++;
  1904.     *pageNumPtr = -1;
  1905.     if (fs_Stats.blockCache.numCacheBlocks > 
  1906.         fs_Stats.blockCache.minCacheBlocks && !List_IsEmpty(lruList)) {
  1907.     fs_Stats.blockCache.triedToGiveToVM++;
  1908.     blockPtr = (Fscache_Block *) USE_LINKS_TO_BLOCK(List_First(lruList));
  1909.     if (blockPtr->timeReferenced < timeLastAccessed) {
  1910.         fs_Stats.blockCache.vmGotPage++;
  1911.         (void) DestroyBlock(TRUE, pageNumPtr);
  1912.     }
  1913.     }
  1914.  
  1915.     UNLOCK_MONITOR;
  1916. }
  1917.  
  1918. /*
  1919.  * ----------------------------------------------------------------------------
  1920.  *
  1921.  * CreateBlock --
  1922.  *
  1923.  *     Add a new block to the list of free blocks.
  1924.  *
  1925.  * Results:
  1926.  *    None.
  1927.  *
  1928.  * Side effects:
  1929.  *    Block removed from the unmapped list and put onto the free list.
  1930.  *
  1931.  * ----------------------------------------------------------------------------
  1932.  */
  1933. INTERNAL static Boolean
  1934. CreateBlock(retBlock, blockPtrPtr)
  1935.     Boolean        retBlock;    /* TRUE => return a pointer to one of 
  1936.                      * the newly created blocks in 
  1937.                      * *blockPtrPtr. */
  1938.     Fscache_Block    **blockPtrPtr;    /* Where to return pointer to block.
  1939.                      * NIL if caller isn't interested. */
  1940. {
  1941.     register    Fscache_Block    *blockPtr;
  1942.     int                newCachePages;
  1943.  
  1944.     if (List_IsEmpty(unmappedList)) {
  1945.     printf( "CreateBlock: No unmapped blocks\n");
  1946.     return(FALSE);
  1947.     }
  1948.     blockPtr = USE_LINKS_TO_BLOCK(List_First(unmappedList));
  1949.     /*
  1950.      * Put memory behind the first available unmapped cache block.
  1951.      */
  1952.     newCachePages = Vm_MapBlock(blockPtr->blockAddr);
  1953.     if (newCachePages == 0) {
  1954.     return(FALSE);
  1955.     }
  1956.     numAvailBlocks += newCachePages * blocksPerPage;
  1957.     fs_Stats.blockCache.numCacheBlocks += newCachePages * blocksPerPage;
  1958.     /*
  1959.      * If we are told to return a block then take it off of the list of
  1960.      * unmapped blocks and let the caller put it onto the appropriate list.
  1961.      * Otherwise put it onto the free list.
  1962.      */
  1963.     if (retBlock) {
  1964.     List_Remove(&blockPtr->useLinks);
  1965.     *blockPtrPtr = blockPtr;
  1966.     } else {
  1967.     fs_Stats.blockCache.numFreeBlocks++;
  1968.     blockPtr->flags = FSCACHE_BLOCK_FREE;
  1969.     List_Move(&blockPtr->useLinks, LIST_ATREAR(totFreeList));
  1970.     }
  1971.     if (PAGE_IS_8K) {
  1972.     /*
  1973.      * Put the other block in the page onto the appropriate free list.
  1974.      */
  1975.     blockPtr = GET_OTHER_BLOCK(blockPtr);
  1976.     blockPtr->flags = FSCACHE_BLOCK_FREE;
  1977.     fs_Stats.blockCache.numFreeBlocks++;
  1978.     if (retBlock) {
  1979.         List_Move(&blockPtr->useLinks, LIST_ATREAR(partFreeList));
  1980.     } else {
  1981.         List_Move(&blockPtr->useLinks, LIST_ATREAR(totFreeList));
  1982.     }
  1983.     }
  1984.     if (! List_IsEmpty(fscacheFullWaitList)) {
  1985.     Fsutil_WaitListNotify(fscacheFullWaitList);
  1986.     }
  1987.     Sync_Broadcast(&cleanBlockCondition);
  1988.  
  1989.     return(TRUE);
  1990. }
  1991.  
  1992.  
  1993. /*
  1994.  * ----------------------------------------------------------------------------
  1995.  *
  1996.  * DestroyBlock --
  1997.  *
  1998.  *     Destroy one physical page worth of blocks.
  1999.  *
  2000.  * Results:
  2001.  *    None.
  2002.  *
  2003.  * Side effects:
  2004.  *    Cache blocks have memory removed from behind them and are moved to
  2005.  *    the unmapped list. 
  2006.  *
  2007.  * ----------------------------------------------------------------------------
  2008.  */
  2009. INTERNAL static Boolean
  2010. DestroyBlock(retOnePage, pageNumPtr)
  2011.     Boolean    retOnePage;
  2012.     int        *pageNumPtr;
  2013. {
  2014.     register    Fscache_Block    *blockPtr;
  2015.     register    Fscache_Block    *otherBlockPtr;
  2016.     int        pages;
  2017.  
  2018.     if ((numAvailBlocks-blocksPerPage <  minNumAvailBlocks) ||
  2019.         (fs_Stats.blockCache.numCacheBlocks - FSCACHE_MIN_BLOCKS - 
  2020.             blocksPerPage < minNumAvailBlocks) ) {
  2021.     return FALSE;
  2022.     }
  2023.     /*
  2024.      * First try the list of totally free pages.
  2025.      */
  2026.     if (!List_IsEmpty(totFreeList)) {
  2027.     blockPtr = USE_LINKS_TO_BLOCK(List_First(totFreeList));
  2028.     pages = Vm_UnmapBlock(blockPtr->blockAddr, retOnePage,
  2029.                   (unsigned int *)pageNumPtr);
  2030.     fs_Stats.blockCache.numCacheBlocks -= pages * blocksPerPage;
  2031.     blockPtr->flags = FSCACHE_NOT_MAPPED;
  2032.     List_Move(&blockPtr->useLinks, LIST_ATREAR(unmappedList));
  2033.     fs_Stats.blockCache.numFreeBlocks--;
  2034.     if (PAGE_IS_8K) {
  2035.         /*
  2036.          * Unmap the other block.  The block address can point to either
  2037.          * of the two blocks.
  2038.          */
  2039.         blockPtr = GET_OTHER_BLOCK(blockPtr);
  2040.         blockPtr->flags = FSCACHE_NOT_MAPPED;
  2041.         List_Move(&blockPtr->useLinks, LIST_ATREAR(unmappedList));
  2042.         fs_Stats.blockCache.numFreeBlocks--;
  2043.     }
  2044.     numAvailBlocks -= pages * blocksPerPage;
  2045.     if (numAvailBlocks < 0) {
  2046.         panic("DestroyBlock numAvailBlocks < 0.\n");
  2047.     }
  2048.     return(TRUE);
  2049.     }
  2050.  
  2051.     /*
  2052.      * Now take blocks from the LRU list until we get one that we can use.
  2053.      */
  2054.     while (TRUE) {
  2055. #ifdef SOSP91
  2056.     unsigned int    flags;
  2057.  
  2058.     if (retOnePage) {
  2059.         flags = FSCACHE_BLOCK_VM;
  2060.     } else {
  2061.         flags = FSCACHE_BLOCK_SHRINK;
  2062.     }
  2063.     blockPtr = FetchBlock(FALSE, FALSE, flags);
  2064. #else
  2065.     blockPtr = FetchBlock(FALSE, FALSE);
  2066. #endif /* SOSP91 */
  2067.     if (blockPtr == (Fscache_Block *) NIL) {
  2068.         /*
  2069.          * There are no clean blocks left so give up.
  2070.          */
  2071.         return(FALSE);
  2072.     }
  2073.     if (PAGE_IS_8K) {
  2074.         /*
  2075.          * We have to deal with the other block.  If it is in use, then
  2076.          * we can't take this page.  Otherwise delete the block from
  2077.          * the cache and put it onto the unmapped list.
  2078. #ifdef SOSP91
  2079.          * If we end up not using the block 'cause the other's in
  2080.          * use, this block still counts as being LRU'd since we end up
  2081.          * putting it on the free list and then continuing.
  2082. #endif SOSP91
  2083.          */
  2084.         otherBlockPtr = GET_OTHER_BLOCK(blockPtr);
  2085.         if (otherBlockPtr->refCount > 0 ||
  2086.         (otherBlockPtr->flags & 
  2087.              (FSCACHE_BLOCK_DIRTY|FSCACHE_BLOCK_BEING_WRITTEN))) {
  2088.         PutOnFreeList(blockPtr);
  2089.         continue;
  2090.         }
  2091.         /*
  2092.          * The other block is cached but not in use.  Delete it.
  2093.          */
  2094.         if (!(otherBlockPtr->flags & FSCACHE_BLOCK_FREE)) {
  2095.         DeleteBlock(otherBlockPtr);
  2096. #ifdef SOSP91
  2097.         /* Count this block as whatever the other one was. */
  2098.         Fscache_AddCleanStats(flags, otherBlockPtr);
  2099. #endif SOSP91
  2100.         }
  2101.         otherBlockPtr->flags = FSCACHE_NOT_MAPPED;
  2102.         List_Move(&otherBlockPtr->useLinks, LIST_ATREAR(unmappedList));
  2103.     }
  2104.     blockPtr->flags = FSCACHE_NOT_MAPPED;
  2105.     List_Insert(&blockPtr->useLinks, LIST_ATREAR(unmappedList));
  2106.     pages = Vm_UnmapBlock(blockPtr->blockAddr, 
  2107.                 retOnePage, (unsigned int *)pageNumPtr);
  2108.     fs_Stats.blockCache.numCacheBlocks -= pages * blocksPerPage;
  2109.     numAvailBlocks -= pages * blocksPerPage;
  2110.     if (numAvailBlocks < 0) {
  2111.         panic("DestroyBlock numAvailBlocks < 0.\n");
  2112.     }
  2113.     return(TRUE);
  2114.     }
  2115. }
  2116.  
  2117.  
  2118. /*
  2119.  * ----------------------------------------------------------------------------
  2120.  *
  2121.  * FetchBlock --
  2122.  *
  2123.  *    Return a pointer to the oldest available block on the lru list.
  2124.  *    If had to sleep because all of memory is dirty then return NIL.
  2125.  *    In this cause our caller has to retry various free lists.
  2126.  *
  2127.  * Results:
  2128.  *    Pointer to oldest available block, NIL if had to wait.  
  2129.  *
  2130.  * Side effects:
  2131.  *    Block deleted.
  2132.  *
  2133.  * ----------------------------------------------------------------------------
  2134.  */
  2135. #ifdef SOSP91
  2136. static INTERNAL Fscache_Block *
  2137. FetchBlock(canWait, cantBlock, flags)
  2138.     Boolean    canWait;    /* TRUE implies can sleep if all of memory is 
  2139.                  * dirty. */
  2140.     Boolean    cantBlock;    /* TRUE if we can't block. */
  2141.     unsigned    int    flags;
  2142. #else
  2143. static INTERNAL Fscache_Block *
  2144. FetchBlock(canWait, cantBlock)
  2145.     Boolean    canWait;    /* TRUE implies can sleep if all of memory is 
  2146.                  * dirty. */
  2147.     Boolean    cantBlock;    /* TRUE if we can't block. */
  2148. #endif /* SOSP91 */
  2149. {
  2150.     register    Fscache_Block    *blockPtr;
  2151.     register    List_Links    *listPtr;
  2152.  
  2153.     if (List_IsEmpty(lruList)) {
  2154.     printf("FetchBlock: LRU list is empty\n");
  2155.     return((Fscache_Block *) NIL);
  2156.     }
  2157.  
  2158.     if ((numAvailBlocks > minNumAvailBlocks) || cantBlock)  {
  2159.     /*
  2160.      * Scan list for unlocked, clean block.
  2161.      */
  2162.     LIST_FORALL(lruList, listPtr) {
  2163.         blockPtr = USE_LINKS_TO_BLOCK(listPtr);
  2164.         if (blockPtr->refCount > 0) {
  2165.         /*
  2166.          * Block is locked.
  2167.          */
  2168.         } else if (blockPtr->flags & 
  2169.                 (FSCACHE_BLOCK_DIRTY|FSCACHE_BLOCK_BEING_WRITTEN)) {
  2170.         /*
  2171.          * Block is dirty or being cleaned.  Mark it so that it will be
  2172.          * freed after it has been cleaned.
  2173.          */
  2174.         blockPtr->flags |= FSCACHE_MOVE_TO_FRONT;
  2175.         if (!(blockPtr->cacheInfoPtr->flags & 
  2176.                     FSCACHE_FILE_BEING_WRITTEN)) {
  2177. #ifdef SOSP91
  2178.             if (flags & FSCACHE_BLOCK_VM) {
  2179.             blockPtr->cacheInfoPtr->flags |= FSCACHE_VM;
  2180.             } else if (flags & FSCACHE_BLOCK_SHRINK) {
  2181.             blockPtr->cacheInfoPtr->flags |= FSCACHE_SHRINK;
  2182.             } else {
  2183.             blockPtr->cacheInfoPtr->flags |= FSCACHE_LRU;
  2184.             }
  2185. #endif SOSP91
  2186.             StartFileSync(blockPtr->cacheInfoPtr);
  2187.         }
  2188.         } else if (blockPtr->flags & FSCACHE_BLOCK_DELETED) {
  2189.         printf( "FetchBlock: deleted block %d of file %d in LRU list\n",
  2190.             blockPtr->blockNum, blockPtr->fileNum);
  2191.         } else  {
  2192.         /*
  2193.          * This block is clean and unlocked.  Delete it from the
  2194.          * hash table and use it.
  2195.          */
  2196. #ifdef SOSP91
  2197.         Fscache_AddCleanStats(flags, blockPtr);
  2198. #endif SOSP91
  2199.         fs_Stats.blockCache.lru++;
  2200.         List_Remove(&blockPtr->useLinks);
  2201.         DeleteBlock(blockPtr);
  2202.         return(blockPtr);
  2203.         }
  2204.     }
  2205.     } else if (numAvailBlocks <= minNumAvailBlocks) {
  2206.      Fscache_Backend    *backendPtr;
  2207.          LIST_FORALL(backendList, (List_Links *) backendPtr) {
  2208.         if (!List_IsEmpty(&backendPtr->dirtyListHdr)) { 
  2209. #ifdef SOSP91
  2210.         /*
  2211.          * XXX Darn - I can't put FSCACHE_SPACE flag into the various
  2212.          * file's cacheInfo structs here, but I'd like to.  This means
  2213.          * that "unknown" may imply "space" in the results.
  2214.          */
  2215. #endif SOSP91
  2216.         StartBackendWriteback(backendPtr);
  2217.         }
  2218.          }
  2219.   }
  2220.     /*
  2221.      * We have looked at every block but we couldn't use any.
  2222.      * If possible wait until the block cleaner cleans a block for us.
  2223.      */
  2224.     if (canWait && !cantBlock) {
  2225.     (void) Sync_Wait(&cleanBlockCondition, FALSE);
  2226.     }
  2227.     return((Fscache_Block *) NIL);
  2228. }
  2229.  
  2230.  
  2231. /*
  2232.  *----------------------------------------------------------------------
  2233.  *
  2234.  * StartBackendWriteback --
  2235.  *
  2236.  *    Start a backend writeback process for the specified backend.
  2237.  *    This routine keeps track the number of backend writebacks 
  2238.  *    active.
  2239.  *
  2240.  * Results:
  2241.  *    None.
  2242.  *
  2243.  * Side effects:
  2244.  *
  2245.  *----------------------------------------------------------------------
  2246.  */
  2247.  
  2248. static void
  2249. StartBackendWriteback(backendPtr)
  2250.     Fscache_Backend *backendPtr;
  2251. {
  2252.     Boolean    started;
  2253.  
  2254.     started = backendPtr->ioProcs.startWriteBack(backendPtr);
  2255.     if (started) {
  2256.     numBackendsActive++;
  2257.     }
  2258.     return;
  2259. }
  2260.  
  2261.  
  2262. /*
  2263.  * ----------------------------------------------------------------------------
  2264.  *
  2265.  *  Fscache_GetDirtyBlock --
  2266.  *
  2267.  *         Take the blocks off of a file's dirty list and return a pointer
  2268.  *    to it.  The synchronization between closing a file and writing
  2269.  *    back its blocks is done here.  If there are no more dirty blocks
  2270.  *    and the file is being closed we poke the closing process.  If
  2271.  *    there are still dirty blocks and someone is closing the file
  2272.  *    we put the file back onto the file dirty list and don't return a block.
  2273.  *
  2274.  * Results:
  2275.  *    The block returned. NIL if no blocks are ready.
  2276.  *
  2277.  * Side effects:
  2278.  *    None.
  2279.  *
  2280.  * ----------------------------------------------------------------------------
  2281.  */
  2282. ENTRY Fscache_Block *
  2283. Fscache_GetDirtyBlock(cacheInfoPtr, blockMatchProc, clientData,
  2284.             lastDirtyBlockPtr)
  2285.     Fscache_FileInfo    *cacheInfoPtr;
  2286.     Boolean        (*blockMatchProc)();
  2287.     ClientData        clientData;
  2288.     int            *lastDirtyBlockPtr;
  2289. {
  2290.     register    List_Links    *dirtyPtr;
  2291.     register    Fscache_Block    *blockPtr;
  2292.  
  2293.     LOCK_MONITOR;
  2294.  
  2295.     *lastDirtyBlockPtr = 0;
  2296.  
  2297.     if (cacheInfoPtr->flags & FSCACHE_CLOSE_IN_PROGRESS) {
  2298.     /*
  2299.      * We can't do any write-backs until the file is closed.
  2300.      * Return zero blocks in hope that the backend will return
  2301.      * the file to the cache.
  2302.      */
  2303.     UNLOCK_MONITOR;
  2304.     return (Fscache_Block *) NIL;
  2305.     }
  2306.     if (cacheInfoPtr->flags & (FSCACHE_SERVER_DOWN | FSCACHE_NO_DISK_SPACE | 
  2307.                   FSCACHE_GENERIC_ERROR|FSCACHE_FILE_GONE)) {
  2308.     UNLOCK_MONITOR;
  2309.     return (Fscache_Block *) NIL;
  2310.     }
  2311.  
  2312.     LIST_FORALL(&cacheInfoPtr->dirtyList, dirtyPtr) {
  2313.     blockPtr = DIRTY_LINKS_TO_BLOCK(dirtyPtr);
  2314.     if (blockPtr->fileNum != cacheInfoPtr->hdrPtr->fileID.minor) {
  2315.         UNLOCK_MONITOR;
  2316.         panic( "GetDirtyBlock, bad block\n");
  2317.         LOCK_MONITOR;
  2318.         continue;
  2319.     }
  2320.     if (blockPtr->refCount > 0) {
  2321.         /*
  2322.          * Being actively used.  Wait until it is not in use anymore in
  2323.          * case the user is writing it for example.
  2324.          */
  2325.         blockPtr->flags |= FSCACHE_BLOCK_CLEANER_WAITING;
  2326.         continue;
  2327.     }
  2328.     if (!blockMatchProc(blockPtr, clientData)) {
  2329.         continue;
  2330.     }
  2331.     /*
  2332.      * Mark the block as being written out and clear the dirty flag in case
  2333.      * someone modifies it while we are writing it out.
  2334.      */
  2335.     List_Remove(dirtyPtr);
  2336.     blockPtr->flags &= ~(FSCACHE_BLOCK_DIRTY|FSCACHE_BLOCK_BEING_CLEANED);
  2337.     blockPtr->flags |= FSCACHE_BLOCK_BEING_WRITTEN;
  2338.     blockPtr->refCount++;
  2339.     if (blockPtr->refCount == 1) { 
  2340.         VmMach_LockCachePage(blockPtr->blockAddr);
  2341.     }
  2342.     /*
  2343.      * Gather statistics.
  2344.      */
  2345.     fs_Stats.blockCache.blocksWrittenThru++;
  2346.     switch (blockPtr->flags &
  2347.         (FSCACHE_DATA_BLOCK | FSCACHE_IND_BLOCK |
  2348.          FSCACHE_DESC_BLOCK | FSCACHE_DIR_BLOCK)) {
  2349.         case FSCACHE_DATA_BLOCK:
  2350.         fs_Stats.blockCache.dataBlocksWrittenThru++;
  2351.         break;
  2352.         case FSCACHE_IND_BLOCK:
  2353.         fs_Stats.blockCache.indBlocksWrittenThru++;
  2354.         break;
  2355.         case FSCACHE_DESC_BLOCK:
  2356.         fs_Stats.blockCache.descBlocksWrittenThru++;
  2357.         break;
  2358.         case FSCACHE_DIR_BLOCK:
  2359.         fs_Stats.blockCache.dirBlocksWrittenThru++;
  2360.         break;
  2361.         default:
  2362.         printf( "Ofs_CleanBlocks: Unknown block type\n");
  2363.     }
  2364.     if (blockPtr->blockSize < 0) {
  2365.         panic( "Fscache_GetDirtyBlock: uninitialized block size\n");
  2366.     }
  2367.     *lastDirtyBlockPtr = List_IsEmpty(&cacheInfoPtr->dirtyList);
  2368.     UNLOCK_MONITOR;
  2369.     return blockPtr;
  2370.     }
  2371.     *lastDirtyBlockPtr = List_IsEmpty(&cacheInfoPtr->dirtyList);
  2372.     UNLOCK_MONITOR;
  2373.     return (Fscache_Block *) NIL;
  2374. }
  2375.  
  2376.  
  2377. /*
  2378.  * ----------------------------------------------------------------------------
  2379.  *
  2380.  * Fscache_ReturnDirtyBlock --
  2381.  *
  2382.  *         This routine will process the newly cleaned blocks.
  2383.  *
  2384.  * Results:
  2385.  *         None.
  2386.  *
  2387.  * Side effects:
  2388.  *    The block may be moved on the allocate list and also possibly freed.
  2389.  *
  2390.  * ----------------------------------------------------------------------------
  2391.  */
  2392. ENTRY void
  2393. Fscache_ReturnDirtyBlock(blockPtr, status)
  2394.     register  Fscache_Block    *blockPtr; /*  blocks to  return. */
  2395.     ReturnStatus        status;
  2396. {
  2397.     register    Fscache_FileInfo    *cacheInfoPtr;
  2398.  
  2399.     LOCK_MONITOR;
  2400.  
  2401.     cacheInfoPtr = blockPtr->cacheInfoPtr;
  2402.  
  2403.     blockPtr->flags &= ~FSCACHE_BLOCK_BEING_WRITTEN;
  2404.     blockPtr->refCount--;
  2405.     if (blockPtr->refCount == 0) {
  2406.     VmMach_UnlockCachePage(blockPtr->blockAddr);
  2407.     }
  2408.     Sync_Broadcast(&blockPtr->ioDone);
  2409.     if (status == GEN_EINTR) {
  2410.     blockPtr->flags |= FSCACHE_BLOCK_DIRTY;
  2411.     } else if (status != SUCCESS) {
  2412.     /*
  2413.      * This block could not be written out.
  2414.      */
  2415.     Boolean        printErrorMsg;
  2416.  
  2417.     printErrorMsg = FALSE;
  2418.     switch (status) {
  2419.         case RPC_TIMEOUT:
  2420.         case FS_STALE_HANDLE:
  2421.         case RPC_SERVICE_DISABLED:    
  2422.         if (!(cacheInfoPtr->flags & FSCACHE_SERVER_DOWN)) {
  2423.             printErrorMsg = TRUE;
  2424.             cacheInfoPtr->flags |= FSCACHE_SERVER_DOWN;
  2425.         }
  2426.         /*
  2427.          * Mark the handle as needing recovery.  Then invoke a
  2428.          * background process to attempt the recovery now.
  2429.          */
  2430.         (void) Fsutil_WantRecovery(cacheInfoPtr->hdrPtr);
  2431.         if (status == FS_STALE_HANDLE) {
  2432.             Proc_CallFunc(Fsutil_AttemptRecovery,
  2433.                   (ClientData)cacheInfoPtr->hdrPtr, 0);
  2434.         }
  2435.         break;
  2436.         case FS_NO_DISK_SPACE:
  2437.         if (!(cacheInfoPtr->flags & FSCACHE_NO_DISK_SPACE)) {
  2438.             printErrorMsg = TRUE;
  2439.             cacheInfoPtr->flags |= FSCACHE_NO_DISK_SPACE;
  2440.         }
  2441.         break;
  2442.         case FS_DOMAIN_UNAVAILABLE:
  2443.         if (!(cacheInfoPtr->flags & FSCACHE_DOMAIN_DOWN)) {
  2444.             printErrorMsg = TRUE;
  2445.             cacheInfoPtr->flags |= FSCACHE_DOMAIN_DOWN;
  2446.         }
  2447.         break;
  2448.         case DEV_RETRY_ERROR:
  2449.         case DEV_HARD_ERROR:
  2450.         /*
  2451.          * Schedule a background process to allocate new space for
  2452.          * this block.  Inc the ref count so the block won't go away
  2453.          * and won't be written again, and mark it as being written so
  2454.          * noone will attempt to change where the block is on disk.
  2455.          */
  2456.         blockPtr->refCount++;
  2457.         if (blockPtr->refCount == 1) { 
  2458.             VmMach_LockCachePage(blockPtr->blockAddr);
  2459.         }
  2460.         blockPtr->flags |= FSCACHE_BLOCK_BEING_WRITTEN;
  2461.         Proc_CallFunc(
  2462.             cacheInfoPtr->backendPtr->ioProcs.reallocBlock, 
  2463.                     (ClientData)blockPtr, 0);
  2464.         printErrorMsg = TRUE;
  2465.         printf("File blk %d phys blk %d: ",
  2466.                 blockPtr->blockNum, blockPtr->diskBlock);
  2467.         cacheInfoPtr->flags |= FSCACHE_GENERIC_ERROR;
  2468.         break;
  2469.         default: 
  2470.         printErrorMsg = TRUE;
  2471.         cacheInfoPtr->flags |= FSCACHE_GENERIC_ERROR;
  2472.         break;
  2473.     }
  2474.     if (printErrorMsg) {
  2475.         Fsutil_FileError(cacheInfoPtr->hdrPtr, 
  2476.             "Write-back failed", status);
  2477.     }
  2478.     cacheInfoPtr->lastTimeTried = Fsutil_TimeInSeconds();
  2479.     PutBlockOnDirtyList(blockPtr, TRUE);
  2480.     UNLOCK_MONITOR;
  2481.     return;
  2482.     }
  2483.     /*
  2484.      * Successfully wrote the block.
  2485.      */
  2486.     cacheInfoPtr->flags &= 
  2487.             ~(FSCACHE_SERVER_DOWN | FSCACHE_NO_DISK_SPACE | 
  2488.               FSCACHE_GENERIC_ERROR);
  2489.     if (blockPtr->flags & FSCACHE_BLOCK_DIRTY) { 
  2490.     PutBlockOnDirtyList(blockPtr, TRUE);
  2491.     } else { 
  2492.     if (blockPtr->refCount == 0) {
  2493.         numAvailBlocks++; 
  2494.     }
  2495.     /* 
  2496.      * Now see if we are supposed to take any special action with this
  2497.      * block once we are done.
  2498.      */
  2499.     if (blockPtr->flags & FSCACHE_BLOCK_DELETED) {
  2500.         cacheInfoPtr->blocksInCache--;
  2501.         List_Remove(&blockPtr->fileLinks);
  2502.         PutOnFreeList(blockPtr);
  2503.     } else if (blockPtr->flags & FSCACHE_MOVE_TO_FRONT) {
  2504.         List_Move(&blockPtr->useLinks, LIST_ATFRONT(lruList));
  2505.         blockPtr->flags &= ~FSCACHE_MOVE_TO_FRONT;
  2506.     }
  2507.  
  2508.     cacheInfoPtr->numDirtyBlocks--;
  2509.     /*
  2510.      * Wakeup the block allocator which may be waiting for us to clean
  2511.      * a block
  2512.      */
  2513.     if (! List_IsEmpty(fscacheFullWaitList)) {
  2514.         Fsutil_WaitListNotify(fscacheFullWaitList);
  2515.     }
  2516.     Sync_Broadcast(&cleanBlockCondition);
  2517.     }
  2518.     UNLOCK_MONITOR;
  2519. }
  2520.  
  2521. /*
  2522.  * ----------------------------------------------------------------------------
  2523.  *
  2524.  *  Fscache_GetDirtyFile --
  2525.  *
  2526.  *         Take a dirty file off of the dirty file list for the
  2527.  *    specified backend. Return a pointer to the cache state for 
  2528.  *    the file return. This routine is used by the backend to
  2529.  *    walk through the dirty list.
  2530.  *
  2531.  * Results:
  2532.  *    A pointer to the cache state for the first file on the dirty list.
  2533.  *    If there are no files then a NIL pointer is returned.  
  2534.  *
  2535.  * Side effects:
  2536.  *      An element is removed from the dirty file list.  The fact that
  2537.  *    the dirty list links are at the beginning of the cacheInfo struct
  2538.  *    is well known and relied on to map from the dirty list to cacheInfoPtr.
  2539.  *
  2540.  * ----------------------------------------------------------------------------
  2541.  */
  2542. ENTRY Fscache_FileInfo *
  2543. Fscache_GetDirtyFile(backendPtr, fsyncOnly, fileMatchProc, clientData)
  2544.     Fscache_Backend    *backendPtr;    /* Cache backend to take dirty files
  2545.                      * from. */
  2546.     Boolean        fsyncOnly;    /* TRUE if we should return only 
  2547.                      * fsynced files. */
  2548.     Boolean        (*fileMatchProc)();    /* File match procedure. */
  2549.     ClientData        clientData;    /* ClientData for match procedure. */
  2550. {
  2551.     register Fscache_FileInfo *cacheInfoPtr;
  2552.  
  2553.     LOCK_MONITOR;
  2554.  
  2555.     /*
  2556.      * If the dirty list is empty return NIL.
  2557.      */
  2558.     if (List_IsEmpty(&backendPtr->dirtyListHdr)) {
  2559.     UNLOCK_MONITOR;
  2560.     return (Fscache_FileInfo *) NIL;
  2561.     }
  2562.  
  2563.     /*
  2564.      * Otherwise, search the the dirtyList picking off the file to return.
  2565.      */
  2566.     LIST_FORALL(&(backendPtr->dirtyListHdr), (List_Links *)cacheInfoPtr) {
  2567.      if (cacheInfoPtr->flags & 
  2568.             (FSCACHE_CLOSE_IN_PROGRESS|FSCACHE_SERVER_DOWN)) {
  2569.         /*
  2570.          * Close in progress on the file the block lives in so we aren't
  2571.          * allowed to write any more blocks.
  2572.          */
  2573.         continue;
  2574.     } else if (cacheInfoPtr->flags & FSCACHE_FILE_GONE) {
  2575.         /*
  2576.          * The file is being deleted.
  2577.          */
  2578.         printf("FscacheGetDirtyFile skipping deleted file <%d,%d> \"%s\"\n",
  2579.         cacheInfoPtr->hdrPtr->fileID.major,
  2580.         cacheInfoPtr->hdrPtr->fileID.minor,
  2581.         Fsutil_HandleName(cacheInfoPtr->hdrPtr));
  2582.         continue;
  2583.     } else if (cacheInfoPtr->flags & 
  2584.                (FSCACHE_NO_DISK_SPACE | FSCACHE_DOMAIN_DOWN |
  2585.                 FSCACHE_GENERIC_ERROR)) {
  2586.         if (Fsutil_TimeInSeconds() - cacheInfoPtr->lastTimeTried <
  2587.             FSUTIL_WRITE_RETRY_INTERVAL) {
  2588.         continue;
  2589.         }
  2590.         cacheInfoPtr->flags &= 
  2591.                 ~(FSCACHE_NO_DISK_SPACE | FSCACHE_DOMAIN_DOWN |
  2592.                   FSCACHE_GENERIC_ERROR);
  2593.     } 
  2594.  
  2595.     if (!fileMatchProc(cacheInfoPtr, clientData)) {
  2596.         continue;
  2597.     }
  2598.     if ((numAvailBlocks > minNumAvailBlocks) && fsyncOnly && 
  2599.         !( (cacheInfoPtr->flags & FSCACHE_FILE_FSYNC) ||
  2600.             (cacheInfoPtr->oldestDirtyBlockTime < filewriteBackTime))) {
  2601.         break;
  2602.     }
  2603.     /*
  2604.      * Check to make sure that if dirty blocks exist then at least one 
  2605.      * of the blocks is available to write.
  2606.      */
  2607.     if (!List_IsEmpty(&cacheInfoPtr->dirtyList)) { 
  2608.         register    List_Links    *dirtyPtr;
  2609.         register    Fscache_Block    *blockPtr;
  2610.         Boolean    found = FALSE;
  2611.         LIST_FORALL(&cacheInfoPtr->dirtyList, dirtyPtr) {
  2612.         blockPtr = DIRTY_LINKS_TO_BLOCK(dirtyPtr);
  2613.         if (blockPtr->refCount == 0) {
  2614.             found = TRUE;
  2615.             break;
  2616.         }
  2617.         }
  2618.         if (!found) {
  2619.         continue;
  2620.         }
  2621.         }
  2622.  
  2623.     cacheInfoPtr->flags |= FSCACHE_FILE_BEING_WRITTEN;
  2624.     cacheInfoPtr->flags &= ~FSCACHE_FILE_ON_DIRTY_LIST;
  2625. #ifdef SOSP91
  2626.     if (fsyncOnly) {
  2627.         cacheInfoPtr->flags |= FSCACHE_TIME;
  2628.     }
  2629.     if (numAvailBlocks <= minNumAvailBlocks) {
  2630.         cacheInfoPtr->flags |= FSCACHE_SPACE;
  2631.     }
  2632. #endif SOSP91
  2633.     List_Remove((List_Links *)cacheInfoPtr);
  2634.     UNLOCK_MONITOR;
  2635.     return cacheInfoPtr;
  2636.     }
  2637.     UNLOCK_MONITOR;
  2638.     return (Fscache_FileInfo *) NIL;
  2639. }
  2640.  
  2641. /*
  2642.  * ----------------------------------------------------------------------------
  2643.  *
  2644.  *  Fscache_ReturnDirtyFile --
  2645.  *
  2646.  *         Return a file checked-out using get dirty file. 
  2647.  *
  2648.  * Results:
  2649.  * Side effects:
  2650.  *
  2651.  * ----------------------------------------------------------------------------
  2652.  */
  2653. ENTRY void
  2654. Fscache_ReturnDirtyFile(cacheInfoPtr, onFront)
  2655.     Fscache_FileInfo    *cacheInfoPtr;    /* File to return. */
  2656.     Boolean         onFront;    /* Put it on the front the of list. */
  2657. {
  2658.  
  2659.     LOCK_MONITOR;
  2660.  
  2661.     /*
  2662.      * Move the from file being written state (and list) to the
  2663.      * dirty list or no list at all.
  2664.      */
  2665.     cacheInfoPtr->flags &= ~FSCACHE_FILE_BEING_WRITTEN;
  2666.     if (cacheInfoPtr->numDirtyBlocks == 0) { 
  2667.     Sync_Broadcast(&cacheInfoPtr->noDirtyBlocks);
  2668.     if (!(cacheInfoPtr->flags & FSCACHE_FILE_DESC_DIRTY)) {
  2669.         cacheInfoPtr->flags &= ~(FSCACHE_FILE_FSYNC |
  2670.                                  FSCACHE_FILE_BEING_CLEANED);
  2671.     }
  2672.     }
  2673.  
  2674.     if (((cacheInfoPtr->numDirtyBlocks > 0) || 
  2675.          (cacheInfoPtr->flags & FSCACHE_FILE_DESC_DIRTY)) &&
  2676.      !(cacheInfoPtr->flags & FSCACHE_FILE_GONE)) {
  2677.     PutFileOnDirtyList(cacheInfoPtr, 
  2678.                 cacheInfoPtr->oldestDirtyBlockTime);
  2679.     }
  2680.     if (cacheInfoPtr->flags & FSCACHE_CLOSE_IN_PROGRESS) {
  2681.     /*
  2682.      * Wake up anyone waiting for us to finish so that they can 
  2683.      * close their file. 
  2684.      */
  2685.     Sync_Broadcast(&closeCondition);
  2686.     }
  2687.  
  2688.     UNLOCK_MONITOR;
  2689.     return;
  2690. }
  2691.  
  2692. /*
  2693.  *----------------------------------------------------------------------
  2694.  *
  2695.  * FscacheBackendIdle --
  2696.  *
  2697.  *    Inform the cache that a backend write-back has finished..
  2698.  *
  2699.  * Results:
  2700.  *    None.
  2701.  *
  2702.  * Side effects:
  2703.  *
  2704.  *----------------------------------------------------------------------
  2705.  */
  2706. /*ARGSUSED*/
  2707. void
  2708. FscacheBackendIdle(backendPtr)
  2709.     Fscache_Backend *backendPtr;
  2710. {
  2711.     LOCK_MONITOR;
  2712.     numBackendsActive--;
  2713.     if (numBackendsActive == 0) {
  2714.     Sync_Broadcast(&writeBackComplete);
  2715.     }
  2716.     UNLOCK_MONITOR;
  2717.     return;
  2718. }
  2719.  
  2720.  
  2721.  
  2722. /*
  2723.  * ----------------------------------------------------------------------------
  2724.  *
  2725.  * FscacheFinishRealloc --
  2726.  *
  2727.  *    After reallocating new space for a block, finish things up.
  2728.  *
  2729.  * Results:
  2730.  *         None.
  2731.  *
  2732.  * Side effects:
  2733.  *         None.
  2734.  *
  2735.  * ----------------------------------------------------------------------------
  2736.  */
  2737. ENTRY void
  2738. FscacheFinishRealloc(blockPtr, diskBlock)
  2739.     Fscache_Block    *blockPtr;
  2740.     int            diskBlock;
  2741. {
  2742.     LOCK_MONITOR;
  2743.  
  2744.     blockPtr->refCount--;
  2745.     if (blockPtr->refCount == 0) { 
  2746.     VmMach_UnlockCachePage(blockPtr->blockAddr);
  2747.     }
  2748.     blockPtr->flags &= ~FSCACHE_BLOCK_BEING_WRITTEN;
  2749.     Sync_Broadcast(&blockPtr->ioDone);
  2750.     if (diskBlock != -1) {
  2751.     blockPtr->diskBlock = diskBlock;
  2752.     blockPtr->cacheInfoPtr->flags &= 
  2753.                 ~(FSCACHE_NO_DISK_SPACE | FSCACHE_DOMAIN_DOWN |
  2754.                   FSCACHE_GENERIC_ERROR);
  2755.     PutFileOnDirtyList(blockPtr->cacheInfoPtr, 
  2756.             blockPtr->cacheInfoPtr->oldestDirtyBlockTime);
  2757.     StartBackendWriteback(blockPtr->cacheInfoPtr->backendPtr);
  2758.     }
  2759.  
  2760.     UNLOCK_MONITOR;
  2761. }
  2762.  
  2763.  
  2764. /*
  2765.  * ----------------------------------------------------------------------------
  2766.  *
  2767.  * Fscache_PreventWriteBacks --
  2768.  *
  2769.  *    Mark this file as a close in progress.  This routine will not
  2770.  *    return until all dirty block cleaners are done writing out blocks
  2771.  *    for this file.  This is called before doing a close on a file
  2772.  *    and is needed to synchronize write-backs and closes so the
  2773.  *    file server knows when it has all the dirty blocks of a file.
  2774.  *
  2775.  * Results:
  2776.  *    The number of dirty blocks in the cache for this file.
  2777.  *    -1 is returned if the file is not cacheable.
  2778.  *
  2779.  * Side effects:
  2780.  *    FSCACHE_CLOSE_IN_PROGRESS flags set in the cacheInfo for this file.
  2781.  *
  2782.  * ----------------------------------------------------------------------------
  2783.  *
  2784.  */
  2785. ENTRY int
  2786. Fscache_PreventWriteBacks(cacheInfoPtr)
  2787.     Fscache_FileInfo *cacheInfoPtr;
  2788. {
  2789.     int    numDirtyBlocks;
  2790.  
  2791.     LOCK_MONITOR;
  2792.  
  2793.     cacheInfoPtr->flags |= FSCACHE_CLOSE_IN_PROGRESS;
  2794.     while (cacheInfoPtr->flags & FSCACHE_FILE_BEING_WRITTEN) {
  2795.     (void)Sync_Wait(&closeCondition, FALSE);
  2796.     }
  2797.     if (cacheInfoPtr->flags & FSCACHE_FILE_NOT_CACHEABLE) {
  2798.     numDirtyBlocks = -1;
  2799.     } else {
  2800.     numDirtyBlocks = cacheInfoPtr->numDirtyBlocks;
  2801.     }
  2802.  
  2803.     UNLOCK_MONITOR;
  2804.  
  2805.     return(numDirtyBlocks);
  2806. }
  2807.  
  2808.  
  2809. /*
  2810.  * ----------------------------------------------------------------------------
  2811.  *
  2812.  * Fscache_AllowWriteBacks --
  2813.  *
  2814.  *    The close that was in progress on this file is now done.  We
  2815.  *    can continue to write back blocks now.
  2816.  *
  2817.  * Results:
  2818.  *    None.
  2819.  *
  2820.  * Side effects:
  2821.  *    FSCACHE_CLOSE_IN_PROGRESS flag cleared from the handle for this file.
  2822.  *    Also if the block cleaner is waiting for us then wake it up.
  2823.  *
  2824.  * ----------------------------------------------------------------------------
  2825.  *
  2826.  */
  2827. ENTRY void
  2828. Fscache_AllowWriteBacks(cacheInfoPtr)
  2829.     register    Fscache_FileInfo *cacheInfoPtr;
  2830. {
  2831.     LOCK_MONITOR;
  2832.  
  2833.     cacheInfoPtr->flags &= ~FSCACHE_CLOSE_IN_PROGRESS;
  2834.     if ((cacheInfoPtr->numDirtyBlocks > 0) || 
  2835.         (cacheInfoPtr->flags & FSCACHE_FILE_DESC_DIRTY)) { 
  2836.     PutFileOnDirtyList(cacheInfoPtr, cacheInfoPtr->oldestDirtyBlockTime);
  2837.     if (cacheInfoPtr->flags & FSCACHE_FILE_FSYNC) {
  2838.         StartBackendWriteback(cacheInfoPtr->backendPtr);
  2839.     }
  2840.     }
  2841.  
  2842.     UNLOCK_MONITOR;
  2843. }
  2844.  
  2845.  
  2846. /*
  2847.  * ----------------------------------------------------------------------------
  2848.  *
  2849.  * Fscache_PutFileOnDirtyList --
  2850.  *
  2851.  *    Put the specified file on the dirty list.
  2852.  *
  2853.  * Results:
  2854.  *    None.
  2855.  *
  2856.  * Side effects:
  2857.  *
  2858.  * ----------------------------------------------------------------------------
  2859.  *
  2860.  */
  2861. ReturnStatus
  2862. Fscache_PutFileOnDirtyList(cacheInfoPtr, flags)
  2863.     register    Fscache_FileInfo *cacheInfoPtr;
  2864.     int        flags;
  2865. {
  2866.     LOCK_MONITOR;
  2867.     cacheInfoPtr->flags |= flags;
  2868.  
  2869.     PutFileOnDirtyList(cacheInfoPtr, Fsutil_TimeInSeconds());
  2870.  
  2871.     UNLOCK_MONITOR;
  2872.     return (SUCCESS);
  2873. }
  2874.  
  2875. /*
  2876.  * ----------------------------------------------------------------------------
  2877.  *
  2878.  * Fscache_RemoveFileFromDirtyList --
  2879.  *
  2880.  *    Remove the specified file on the dirty list.
  2881.  *
  2882.  * Results:
  2883.  *    None.
  2884.  *
  2885.  * Side effects:
  2886.  *
  2887.  * ----------------------------------------------------------------------------
  2888.  *
  2889.  */
  2890. ReturnStatus
  2891. Fscache_RemoveFileFromDirtyList(cacheInfoPtr)
  2892.     register    Fscache_FileInfo *cacheInfoPtr;
  2893. {
  2894.     int            blocksInCache;
  2895.  
  2896.     LOCK_MONITOR;
  2897.     while (cacheInfoPtr->flags & FSCACHE_FILE_BEING_WRITTEN) {
  2898.     Sync_Wait(&cacheInfoPtr->noDirtyBlocks, FALSE);
  2899.     }
  2900.     if (cacheInfoPtr->flags & FSCACHE_FILE_ON_DIRTY_LIST) {
  2901.     cacheInfoPtr->flags &= ~(FSCACHE_FILE_ON_DIRTY_LIST|FSCACHE_FILE_FSYNC|
  2902.             FSCACHE_FILE_BEING_CLEANED);
  2903.     List_Remove((List_Links *)cacheInfoPtr);
  2904.     }
  2905.     blocksInCache = cacheInfoPtr->blocksInCache;
  2906.     UNLOCK_MONITOR;
  2907.  
  2908.     if (blocksInCache != 0) {
  2909.     panic("Fscache_RemoveFileFromDirtyList blocks in cache\n");
  2910.     }
  2911.     return SUCCESS;
  2912. }
  2913.  
  2914.  
  2915.  
  2916. /*
  2917.  * ----------------------------------------------------------------------------
  2918.  *
  2919.  *    Miscellaneous functions.
  2920.  *
  2921.  * ----------------------------------------------------------------------------
  2922.  */
  2923.  
  2924. /*
  2925.  * ----------------------------------------------------------------------------
  2926.  *
  2927.  * FscacheAllBlocksInCache --
  2928.  *
  2929.  *     Return true if all of this files blocks are in the cache.  This
  2930.  *    is used to optimize out read ahead.
  2931.  *
  2932.  * Results:
  2933.  *    TRUE if all blocks for the file are in the cache.
  2934.  *
  2935.  * Side effects:
  2936.  *    None.
  2937.  *
  2938.  * ----------------------------------------------------------------------------
  2939.  */
  2940. ENTRY Boolean
  2941. FscacheAllBlocksInCache(cacheInfoPtr)
  2942.     register    Fscache_FileInfo *cacheInfoPtr;
  2943. {
  2944.     Boolean    result;
  2945.     int        numBlocks;
  2946.  
  2947.     LOCK_MONITOR;
  2948.  
  2949.     fs_Stats.blockCache.allInCacheCalls++;
  2950.     if (cacheInfoPtr->attr.lastByte == -1) {
  2951.     result = TRUE;
  2952.     } else {
  2953.     if (cacheInfoPtr->attr.firstByte == -1) {
  2954.         cacheInfoPtr->attr.firstByte = 0;
  2955.     }
  2956.     numBlocks = (cacheInfoPtr->attr.lastByte/FS_BLOCK_SIZE) -
  2957.             (cacheInfoPtr->attr.firstByte/FS_BLOCK_SIZE) + 1;
  2958.     result = (numBlocks == cacheInfoPtr->blocksInCache);
  2959.     if (result) {
  2960.         fs_Stats.blockCache.allInCacheTrue++;
  2961.     }
  2962.     }
  2963.  
  2964.     UNLOCK_MONITOR;
  2965.  
  2966.     return(result);
  2967. }
  2968.  
  2969. /*
  2970.  * ----------------------------------------------------------------------------
  2971.  *
  2972.  * Fscache_ReserveBlocks --
  2973.  *
  2974.  *   Insure that at least the specified number of cache blocks will be 
  2975.  *   available to Fetch_Block() calls the the FSCACHE_CANT_BLOCK flag.
  2976.  *
  2977.  * Results:
  2978.  *    The number of blocks made available.
  2979.  *
  2980.  * Side effects:
  2981.  *    None.
  2982.  *
  2983.  * ----------------------------------------------------------------------------
  2984.  */
  2985. int
  2986. Fscache_ReserveBlocks(backendPtr, numResBlocks, numNonResBlocks)
  2987.     Fscache_Backend    *backendPtr;
  2988.     int            numResBlocks;
  2989.     int            numNonResBlocks;
  2990. {
  2991.     int    numBlocks = numResBlocks + numNonResBlocks;
  2992.  
  2993.     LOCK_MONITOR;
  2994.     if (numBlocks > fs_Stats.blockCache.maxNumBlocks) {
  2995.     numBlocks = fs_Stats.blockCache.maxNumBlocks - minNumAvailBlocks;
  2996.     }
  2997.  
  2998.     while (fs_Stats.blockCache.numCacheBlocks < minNumAvailBlocks + numBlocks) { 
  2999.     if (!CreateBlock(FALSE, (Fscache_Block **) NIL)) {
  3000.         break;
  3001.     } 
  3002.     }
  3003.     if (minNumAvailBlocks + numResBlocks > 
  3004.         fs_Stats.blockCache.numCacheBlocks - numNonResBlocks) {
  3005.     numResBlocks = fs_Stats.blockCache.numCacheBlocks - numNonResBlocks -
  3006.             minNumAvailBlocks;
  3007.     if (numResBlocks< 0) {
  3008.         numResBlocks = 0;
  3009.     }
  3010.     }
  3011.     minNumAvailBlocks += numResBlocks;
  3012.     while (minNumAvailBlocks > numAvailBlocks) {
  3013.     (void) Sync_Wait(&cleanBlockCondition, FALSE);
  3014.     }
  3015.     UNLOCK_MONITOR;
  3016.     return numResBlocks;
  3017. }
  3018.  
  3019.  
  3020. /*
  3021.  * ----------------------------------------------------------------------------
  3022.  *
  3023.  * Fscache_ReleaseReserveBlocks
  3024.  *
  3025.  *   Release blocks that were Reserved.
  3026.  *
  3027.  * Results:
  3028.  *    None.
  3029.  *
  3030.  * Side effects:
  3031.  *    None.
  3032.  *
  3033.  * ----------------------------------------------------------------------------
  3034.  */
  3035. void
  3036. Fscache_ReleaseReserveBlocks(backendPtr, numBlocks)
  3037.     Fscache_Backend    *backendPtr;
  3038.     int            numBlocks;
  3039. {
  3040.     LOCK_MONITOR;
  3041.  
  3042.     minNumAvailBlocks  -= numBlocks;
  3043.     Sync_Broadcast(&cleanBlockCondition);
  3044.  
  3045.     UNLOCK_MONITOR;
  3046.     return;
  3047. }
  3048.  
  3049.  
  3050. /*
  3051.  * ----------------------------------------------------------------------------
  3052.  *
  3053.  * FscacheBlockOkToScavenge --
  3054.  *
  3055.  *    Decide if it is safe to scavenge the file handle.  This is
  3056.  *    called from Fscache_OkToScavenge which
  3057.  *    has already grabbed the per-file cache lock.
  3058.  *
  3059.  * Results:
  3060.  *    TRUE if there are no blocks (clean or dirty) in the cache for this file.
  3061.  *
  3062.  * Side effects:
  3063.  *    None.
  3064.  *
  3065.  * ----------------------------------------------------------------------------
  3066.  */
  3067. ENTRY int
  3068. FscacheBlockOkToScavenge(cacheInfoPtr)
  3069.     register Fscache_FileInfo    *cacheInfoPtr;    /* Cache state to check. */
  3070. {
  3071.     register int numBlocks;
  3072.     register int numBlocksCheck = 0;
  3073.     register Boolean ok;
  3074.     register Fscache_Block *blockPtr;
  3075.     List_Links        *linkPtr;
  3076.  
  3077.     LOCK_MONITOR;
  3078.     numBlocks = cacheInfoPtr->blocksInCache;
  3079.     LIST_FORALL(&cacheInfoPtr->blockList, (List_Links *)linkPtr) {
  3080.     blockPtr = FILE_LINKS_TO_BLOCK(linkPtr);
  3081.     if (blockPtr->fileNum != cacheInfoPtr->hdrPtr->fileID.minor) {
  3082.         UNLOCK_MONITOR;
  3083.         panic( "FsCacheFileBlocks, bad block\n");
  3084.         return(FALSE);
  3085.     }
  3086.     numBlocksCheck++;
  3087.     }
  3088.     LIST_FORALL(&cacheInfoPtr->indList, (List_Links *)linkPtr) {
  3089.     numBlocksCheck++;
  3090.     }
  3091.     if (numBlocksCheck != numBlocks) {
  3092.     UNLOCK_MONITOR;
  3093.     panic( "FsCacheFileBlocks, wrong block count\n");
  3094.     return(FALSE);
  3095.     }
  3096.     ok = (numBlocks == 0) &&
  3097.     ((cacheInfoPtr->flags & 
  3098.         (FSCACHE_FILE_ON_DIRTY_LIST|FSCACHE_FILE_BEING_WRITTEN)) == 0);
  3099.     UNLOCK_MONITOR;
  3100.     return(ok);
  3101. }
  3102.  
  3103. /*
  3104.  * ----------------------------------------------------------------------------
  3105.  *
  3106.  * Fscache_FileInfoSyncLockCleanup --
  3107.  *
  3108.  *     Clean up Sync_Lock tracing for the cache lock.
  3109.  *
  3110.  * Results:
  3111.  *    None.
  3112.  *
  3113.  * Side effects:
  3114.  *    Set up the fields of the Fscache_FileInfo struct.
  3115.  *
  3116.  * ----------------------------------------------------------------------------
  3117.  */
  3118. /*ARGSUSED*/
  3119. void
  3120. Fscache_FileInfoSyncLockCleanup(cacheInfoPtr)
  3121.     Fscache_FileInfo *cacheInfoPtr;
  3122. {
  3123.     Sync_LockClear(&cacheInfoPtr->lock);
  3124. }
  3125.  
  3126.  
  3127. /*
  3128.  * ----------------------------------------------------------------------------
  3129.  * 
  3130.  *    List Functions
  3131.  *
  3132.  * Functions to put objects onto the free and dirty lists.
  3133.  *
  3134.  * ----------------------------------------------------------------------------
  3135.  */
  3136.  
  3137. /*
  3138.  * ----------------------------------------------------------------------------
  3139.  *
  3140.  * PutOnFreeList --
  3141.  *
  3142.  *     Put the given block onto one of the two free lists.
  3143.  *
  3144.  * Results:
  3145.  *    None.
  3146.  *
  3147.  * Side effects:
  3148.  *    Block either added to the partially free list or the totally free list.
  3149.  *    The list of processes waiting on the full cache is notified if
  3150.  *    is non-empty.
  3151.  *
  3152.  * ----------------------------------------------------------------------------
  3153.  */
  3154. INTERNAL static void
  3155. PutOnFreeList(blockPtr)
  3156.     register    Fscache_Block    *blockPtr;
  3157. {
  3158.     register    Fscache_Block    *otherBlockPtr;
  3159.  
  3160.     blockPtr->flags = FSCACHE_BLOCK_FREE;
  3161.     fs_Stats.blockCache.numFreeBlocks++;
  3162.     if (PAGE_IS_8K) {
  3163.     /*
  3164.      * If all blocks in the page are free then put this block onto the
  3165.      * totally free list.  Otherwise it goes onto the partially free list.
  3166.      */
  3167.     otherBlockPtr = GET_OTHER_BLOCK(blockPtr);
  3168.     if (otherBlockPtr->flags & FSCACHE_BLOCK_FREE) {
  3169.         List_Insert(&blockPtr->useLinks, LIST_ATFRONT(totFreeList));
  3170.         List_Move(&otherBlockPtr->useLinks, LIST_ATFRONT(totFreeList));
  3171.     } else {
  3172.         List_Insert(&blockPtr->useLinks, LIST_ATFRONT(partFreeList));
  3173.     }
  3174.     } else {
  3175.     List_Insert(&blockPtr->useLinks, LIST_ATFRONT(totFreeList));
  3176.     }
  3177.     if (! List_IsEmpty(fscacheFullWaitList)) {
  3178.     Fsutil_WaitListNotify(fscacheFullWaitList);
  3179.     }
  3180.     Sync_Broadcast(&cleanBlockCondition);
  3181. }    
  3182.  
  3183.  
  3184. /*
  3185.  * ----------------------------------------------------------------------------
  3186.  *
  3187.  * PutFileOnDirtyList --
  3188.  *
  3189.  *     Put the given file onto it's backend's dirty list.
  3190.  *
  3191.  * Results:
  3192.  *    None.
  3193.  *
  3194.  * Side effects:
  3195.  *    File's backend's dirty list is modified.
  3196.  *
  3197.  * ----------------------------------------------------------------------------
  3198.  */
  3199. INTERNAL static void
  3200. PutFileOnDirtyList(cacheInfoPtr, oldestDirtyBlockTime)
  3201.     register Fscache_FileInfo    *cacheInfoPtr;    /* Cache info for a file */
  3202.     time_t    oldestDirtyBlockTime;
  3203. {
  3204.     register List_Links    *linkPtr;
  3205.     List_Links    *dirtyList, *place;
  3206.  
  3207.     dirtyList = &cacheInfoPtr->backendPtr->dirtyListHdr;
  3208.     if (!(cacheInfoPtr->flags & 
  3209.     (FSCACHE_FILE_ON_DIRTY_LIST|FSCACHE_FILE_BEING_WRITTEN))) {
  3210.         List_Insert((List_Links *)cacheInfoPtr, LIST_ATREAR(dirtyList));
  3211.     if (cacheInfoPtr->flags & FSCACHE_FILE_FSYNC) {
  3212.         /*
  3213.          * Move down the list until we reach the file or the first non
  3214.          * synced file. 
  3215.          */
  3216.         place = (List_Links *) cacheInfoPtr;
  3217.         LIST_FORALL(dirtyList, linkPtr) {
  3218.         if ((linkPtr == (List_Links *) cacheInfoPtr) ||
  3219.             !(((Fscache_FileInfo *) linkPtr)->flags & 
  3220.                     FSCACHE_FILE_FSYNC)) {
  3221.             place = linkPtr;
  3222.             break;
  3223.         }
  3224.         }
  3225.         if (place != (List_Links *) cacheInfoPtr) {
  3226.         List_Move((List_Links *)cacheInfoPtr, LIST_BEFORE(place));
  3227.         }
  3228.     }
  3229.     cacheInfoPtr->oldestDirtyBlockTime = oldestDirtyBlockTime;
  3230.     cacheInfoPtr->flags |= FSCACHE_FILE_ON_DIRTY_LIST;
  3231.     }
  3232. }
  3233.  
  3234.  
  3235. /*
  3236.  * ----------------------------------------------------------------------------
  3237.  *
  3238.  * PutBlockOnDirtyList --
  3239.  *
  3240.  *     Put the given block onto the dirty list for the file, and make
  3241.  *    sure that the file is on the list of dirty files.
  3242.  *
  3243.  * Results:
  3244.  *    None.
  3245.  *
  3246.  * Side effects:
  3247.  *    The block goes onto the dirty list of the file.
  3248.  *
  3249.  * ----------------------------------------------------------------------------
  3250.  */
  3251. INTERNAL static void
  3252. PutBlockOnDirtyList(blockPtr, onFront)
  3253.     register    Fscache_Block    *blockPtr;    /* Block to put on list. */
  3254.     Boolean    onFront;            /* Put block on front not
  3255.                          * rear of list. */
  3256. {
  3257.     register Fscache_FileInfo *cacheInfoPtr = blockPtr->cacheInfoPtr;
  3258.  
  3259.     blockPtr->flags |= FSCACHE_BLOCK_DIRTY;
  3260.     List_Insert(&blockPtr->dirtyLinks, 
  3261.         onFront ? LIST_ATFRONT(&cacheInfoPtr->dirtyList) :
  3262.               LIST_ATREAR(&cacheInfoPtr->dirtyList));
  3263.  
  3264.     PutFileOnDirtyList(cacheInfoPtr, blockPtr->timeDirtied);
  3265. }
  3266.  
  3267.  
  3268.  
  3269.  
  3270. /*
  3271.  * ----------------------------------------------------------------------------
  3272.  *
  3273.  * GetUnlockedBlock --
  3274.  *
  3275.  *    Retrieve a block from the hash table.  This routine will not return
  3276.  *    until the block is unlocked and is not being written.
  3277.  *
  3278.  * Results:
  3279.  *    Pointer to hash table entry for the block.
  3280.  *
  3281.  * Side effects:
  3282.  *    None.
  3283.  *
  3284.  * ----------------------------------------------------------------------------
  3285.  */
  3286.  
  3287. INTERNAL static Hash_Entry *
  3288. GetUnlockedBlock(blockHashKeyPtr, blockNum)
  3289.     register    BlockHashKey    *blockHashKeyPtr;
  3290.     int                blockNum;
  3291. {
  3292.     register    Fscache_Block    *blockPtr;
  3293.     register    Hash_Entry    *hashEntryPtr;
  3294.  
  3295.     /*
  3296.      * See if block is in the hash table.
  3297.      */
  3298.     blockHashKeyPtr->blockNumber = blockNum;
  3299. again:
  3300.     hashEntryPtr = Hash_LookOnly(blockHashTable, (Address)blockHashKeyPtr);
  3301.     if (hashEntryPtr == (Hash_Entry *) NIL) {
  3302.     return((Hash_Entry *) NIL);
  3303.     }
  3304.  
  3305.     blockPtr = (Fscache_Block *) Hash_GetValue(hashEntryPtr);
  3306.     /*
  3307.      * Wait until the block is unlocked.  Once wake up start over because
  3308.      * the block could have been freed while we were asleep.
  3309.      */
  3310.     if (blockPtr->refCount > 0 || 
  3311.     (blockPtr->flags & FSCACHE_BLOCK_BEING_WRITTEN)) {
  3312.     (void) Sync_Wait(&blockPtr->ioDone, FALSE);
  3313.     if (sys_ShuttingDown) {
  3314.         return((Hash_Entry *) NIL);
  3315.     }
  3316.     goto again;
  3317.     }
  3318.     return(hashEntryPtr);
  3319. }
  3320.  
  3321.  
  3322. /*
  3323.  * ----------------------------------------------------------------------------
  3324.  *
  3325.  * DeleteBlock --
  3326.  *
  3327.  *    Remove the block from the hash table.
  3328.  *
  3329.  * Results:
  3330.  *    None.    
  3331.  *
  3332.  * Side effects:
  3333.  *    Decrement count of blocks in cache for file and block deleted from
  3334.  *    hash table.
  3335.  *
  3336.  * ----------------------------------------------------------------------------
  3337.  *
  3338.  */
  3339. static Fscache_Block *deletedBlockPtr;
  3340.  
  3341. INTERNAL static void
  3342. DeleteBlock(blockPtr)
  3343.     register    Fscache_Block    *blockPtr;
  3344. {
  3345.     BlockHashKey    blockHashKey;
  3346.     register Hash_Entry    *hashEntryPtr;
  3347.  
  3348.     SET_BLOCK_HASH_KEY(blockHashKey, blockPtr->cacheInfoPtr,
  3349.                      blockPtr->blockNum);
  3350.     hashEntryPtr = Hash_LookOnly(blockHashTable, (Address) &blockHashKey);
  3351.     if (hashEntryPtr == (Hash_Entry *) NIL) {
  3352.     UNLOCK_MONITOR;
  3353.     deletedBlockPtr = blockPtr;
  3354.     panic("DeleteBlock: Block in LRU list is not in the hash table.\n");
  3355.     LOCK_MONITOR;
  3356.     return;
  3357.     }
  3358.     Hash_Delete(blockHashTable, hashEntryPtr);
  3359.     blockPtr->cacheInfoPtr->blocksInCache--;
  3360.     List_Remove(&blockPtr->fileLinks);
  3361. }
  3362.  
  3363.  
  3364. /*
  3365.  * ----------------------------------------------------------------------------
  3366.  *
  3367.  * Fscache_DumpStats --
  3368.  *
  3369.  *    Print out the cache statistics.
  3370.  *
  3371.  * Results:
  3372.  *         None.
  3373.  *
  3374.  * Side effects:
  3375.  *         None.
  3376.  *
  3377.  * ----------------------------------------------------------------------------
  3378.  */
  3379. void
  3380. Fscache_DumpStats(dummy)
  3381.     ClientData dummy;        /* unused; see dump.c:eventTable */
  3382. {
  3383.     register Fs_BlockCacheStats *block;
  3384.  
  3385.     block = &fs_Stats.blockCache;
  3386.  
  3387.     printf("\n");
  3388.     printf("READ  %d dirty hits %d clean hits %d zero fill %d\n",
  3389.         block->readAccesses,
  3390.         block->readHitsOnDirtyBlock,
  3391.         block->readHitsOnCleanBlock,
  3392.         block->readZeroFills);
  3393.     printf("WRITE %d p-hits %d p-misses %d thru %d zero %d/%d app %d over %d\n",
  3394.         block->writeAccesses,
  3395.         block->partialWriteHits,
  3396.         block->partialWriteMisses,
  3397.         block->blocksWrittenThru,
  3398.         block->writeZeroFills1, block->writeZeroFills2,
  3399.         block->appendWrites,
  3400.         block->overWrites);
  3401.     if (block->fragAccesses != 0) {
  3402.     printf("FRAG upgrades %d hits %d zero fills\n",
  3403.             block->fragAccesses,
  3404.             block->fragHits,
  3405.             block->fragZeroFills);
  3406.     }
  3407.     if (block->fileDescReads != 0) {
  3408.     printf("FILE DESC reads %d hits %d writes %d hits %d\n", 
  3409.             block->fileDescReads, block->fileDescReadHits,
  3410.             block->fileDescWrites, block->fileDescWriteHits);
  3411.     }
  3412.     if (block->indBlockAccesses != 0) {
  3413.     printf("INDIRECT BLOCKS Accesses %d hits %d\n", 
  3414.             block->indBlockAccesses, block->indBlockHits);
  3415.     }
  3416.     printf("VM asked %d, we tried %d, gave up %d\n",
  3417.         block->vmRequests, block->triedToGiveToVM, block->vmGotPage);
  3418.     printf("BLOCK free %d new %d lru %d part free %d\n",
  3419.         block->totFree, block->unmapped,
  3420.         block->lru, block->partFree);
  3421.     printf("SIZES Blocks min %d num %d max %d, Blocks max %d free %d pitched %d\n",
  3422.         block->minCacheBlocks, block->numCacheBlocks,
  3423.         block->maxCacheBlocks, block->maxNumBlocks,
  3424.         block->numFreeBlocks, block->blocksPitched);
  3425.  
  3426.     printf("OBJECTS stream %d (clt %d) file %d dir %d rmtFile %d pipe %d\n",
  3427.         fs_Stats.object.streams, fs_Stats.object.streamClients,
  3428.         fs_Stats.object.files, fs_Stats.object.directory,
  3429.         fs_Stats.object.rmtFiles, fs_Stats.object.pipes);
  3430.     printf("OBJECTS dev %d pdevControl %d pdev %d remote %d Total %d\n",
  3431.         fs_Stats.object.devices, fs_Stats.object.controls,
  3432.         fs_Stats.object.pseudoStreams, fs_Stats.object.remote,
  3433.         fs_Stats.object.streams + fs_Stats.object.files +
  3434.         fs_Stats.object.rmtFiles + fs_Stats.object.pipes +
  3435.         fs_Stats.object.devices + fs_Stats.object.controls +
  3436.         fs_Stats.object.directory +
  3437.         2 * fs_Stats.object.pseudoStreams + fs_Stats.object.remote);
  3438.     printf("HANDLES max %d exist %d. In %d scans replaced %d of %d (dirs %d)\n",
  3439.         fs_Stats.handle.maxNumber, fs_Stats.handle.exists,
  3440.         fs_Stats.handle.lruScans, fs_Stats.handle.lruHits,
  3441.         fs_Stats.handle.lruChecks, fs_Stats.object.dirFlushed);
  3442. }
  3443.  
  3444.  
  3445. /*
  3446.  * ----------------------------------------------------------------------------
  3447.  *
  3448.  * Fscache_CheckFragmentation --
  3449.  *
  3450.  *    Scan through the cache determining the number of bytes wasted
  3451.  *    compared to a fully variable cache and a cache with 1024 byte blocks.
  3452.  *
  3453.  * Results:
  3454.  *    The number of blocks in the cache, number of bytes wasted.
  3455.  *
  3456.  * Side effects:
  3457.  *    None.
  3458.  *
  3459.  * ----------------------------------------------------------------------------
  3460.  *
  3461.  */
  3462. ENTRY void
  3463. Fscache_CheckFragmentation(numBlocksPtr, totalBytesWastedPtr, fragBytesWastedPtr)
  3464.     int    *numBlocksPtr;        /* Return number of blocks in the cache. */
  3465.     int    *totalBytesWastedPtr;    /* Return the total number of bytes wasted in
  3466.                  * the cache. */
  3467.     int    *fragBytesWastedPtr;    /* Return the number of bytes wasted when cache
  3468.                  * is caches 1024 byte fragments. */
  3469. {
  3470.     register Fscache_Block     *blockPtr;
  3471.     register List_Links          *listPtr, *lPtr;
  3472.     register int        numBlocks = 0;
  3473.     register int        totalBytesWasted = 0;
  3474.     register int        fragBytesWasted = 0;
  3475.     register int        bytesInBlock;
  3476.     int                numFrags;
  3477.  
  3478.     LOCK_MONITOR;
  3479.  
  3480.     listPtr = lruList;
  3481.     LIST_FORALL(listPtr, lPtr) {
  3482.     blockPtr = USE_LINKS_TO_BLOCK(lPtr);
  3483.     if ((blockPtr->refCount > 0) || (blockPtr->blockSize < 0)) {
  3484.         /* 
  3485.          * Skip locked blocks because they might be in the process of
  3486.          * being modified.
  3487.          */
  3488.         continue;
  3489.     }
  3490.     numBlocks++;
  3491.     bytesInBlock = blockPtr->blockSize;
  3492.     if (bytesInBlock < FS_BLOCK_SIZE) {
  3493.         totalBytesWasted += FS_BLOCK_SIZE - bytesInBlock;
  3494.         if (blockPtr->blockNum < FSDM_NUM_DIRECT_BLOCKS) {
  3495.         numFrags = (bytesInBlock - 1) / FS_FRAGMENT_SIZE + 1; 
  3496.         fragBytesWasted += FS_BLOCK_SIZE - numFrags * FS_FRAGMENT_SIZE;
  3497.         }
  3498.     }
  3499.     }
  3500.  
  3501.     *numBlocksPtr = numBlocks;
  3502.     *totalBytesWastedPtr = totalBytesWasted;
  3503.     *fragBytesWastedPtr = fragBytesWasted;
  3504.  
  3505.     UNLOCK_MONITOR;
  3506. }
  3507.  
  3508.  
  3509. /*
  3510.  * ----------------------------------------------------------------------------
  3511.  *
  3512.  * Fscache_CountBlocks --
  3513.  *
  3514.  *    Count the number of clean and dirty blocks under the specified 
  3515.  *    domain.
  3516.  *
  3517.  * Results:
  3518.  *    
  3519.  *
  3520.  * Side effects:
  3521.  *
  3522.  * ----------------------------------------------------------------------------
  3523.  *
  3524.  */
  3525. ENTRY void
  3526. Fscache_CountBlocks(serverID, majorNumber, numBlocksPtr, numDirtyBlocksPtr)
  3527.     int        serverID; /* ServerID of file. */
  3528.     int        majorNumber;  /* Major number of domain */
  3529.     int        *numBlocksPtr; /* OUT: Number of blocks in the cache. */
  3530.     int        *numDirtyBlocksPtr;  /* OUT: Number of dirty blocks in cache.*/
  3531.  
  3532. {
  3533.     register    Fscache_Block    *blockPtr;
  3534.     register    List_Links    *listPtr;
  3535.  
  3536.     LOCK_MONITOR;
  3537.  
  3538.     *numBlocksPtr = *numDirtyBlocksPtr = 0;
  3539.     LIST_FORALL(lruList, listPtr) {
  3540.     blockPtr = USE_LINKS_TO_BLOCK(listPtr);
  3541.     if ((blockPtr->cacheInfoPtr->hdrPtr->fileID.major != majorNumber) ||
  3542.         (blockPtr->cacheInfoPtr->hdrPtr->fileID.serverID != serverID)) {
  3543.         continue;
  3544.     }
  3545.     (*numBlocksPtr)++;
  3546.     if (blockPtr->flags & 
  3547.             (FSCACHE_BLOCK_DIRTY|FSCACHE_BLOCK_BEING_WRITTEN)) {
  3548.         (*numDirtyBlocksPtr)++;
  3549.     } 
  3550.     }
  3551.  
  3552.     UNLOCK_MONITOR;
  3553. }
  3554.  
  3555. #ifdef SOSP91
  3556.  
  3557. Fscache_ExtraStats    fscache_ExtraStats;
  3558.  
  3559.  
  3560. /*
  3561.  *----------------------------------------------------------------------
  3562.  *
  3563.  * Fscache_AddBlockToStats --
  3564.  *
  3565.  *    Count another block as being written out from the cache.
  3566.  *    Record the reason why from its flags.
  3567.  *
  3568.  * Results:
  3569.  *    None.
  3570.  *
  3571.  * Side effects:
  3572.  *    Stats updated.
  3573.  *
  3574.  *----------------------------------------------------------------------
  3575.  */
  3576. void
  3577. Fscache_AddBlockToStats(cacheInfoPtr, blockPtr)
  3578.     Fscache_FileInfo    *cacheInfoPtr;
  3579.     Fscache_Block    *blockPtr;
  3580. {
  3581.     int        now;
  3582.     time_t    dirtyLifeTime;
  3583.  
  3584.     now = Fsutil_TimeInSeconds();
  3585.     dirtyLifeTime = now - blockPtr->timeDirtied;
  3586.  
  3587.     if ((cacheInfoPtr->flags & FSCACHE_REASON_FLAGS) == 0) {
  3588.     fscache_ExtraStats.unknown++;
  3589.     fscache_ExtraStats.unDLife += dirtyLifeTime;
  3590.     return;
  3591.     }
  3592.     if (cacheInfoPtr->flags & FSCACHE_CONSIST_WB) {
  3593.     fscache_ExtraStats.consistWB++;
  3594.     fscache_ExtraStats.cwbDLife += dirtyLifeTime;
  3595.     } else if (cacheInfoPtr->flags & FSCACHE_CONSIST_WBINV) {
  3596.     fscache_ExtraStats.consistWBInv++;
  3597.     fscache_ExtraStats.cwbiDLife += dirtyLifeTime;
  3598.     } else if (cacheInfoPtr->flags & FSCACHE_SYNC) {
  3599.     fscache_ExtraStats.sync++;
  3600.     fscache_ExtraStats.syncDLife += dirtyLifeTime;
  3601.     } else if (cacheInfoPtr->flags & FSCACHE_VM) {
  3602.     fscache_ExtraStats.vm++;
  3603.     fscache_ExtraStats.vmDLife += dirtyLifeTime;
  3604.     } else if (cacheInfoPtr->flags & FSCACHE_SHRINK) {
  3605.     fscache_ExtraStats.shrink++;
  3606.     fscache_ExtraStats.shrinkDLife += dirtyLifeTime;
  3607.     } else if (cacheInfoPtr->flags & FSCACHE_REOPEN) {
  3608.     fscache_ExtraStats.reopen++;
  3609.     fscache_ExtraStats.reDLife += dirtyLifeTime;
  3610.     } else if (cacheInfoPtr->flags & FSCACHE_DETACH) {
  3611.     fscache_ExtraStats.detach++;
  3612.     fscache_ExtraStats.detDLife += dirtyLifeTime;
  3613.     } else if (cacheInfoPtr->flags & FSCACHE_TIME) {
  3614.     fscache_ExtraStats.time++;
  3615.     fscache_ExtraStats.timeDLife += dirtyLifeTime;
  3616.     } else if (cacheInfoPtr->flags & FSCACHE_SPACE) {
  3617.     fscache_ExtraStats.space++;
  3618.     fscache_ExtraStats.spaceDLife += dirtyLifeTime;
  3619.     } else if (cacheInfoPtr->flags & FSCACHE_DESC) {
  3620.     fscache_ExtraStats.desc++;
  3621.     fscache_ExtraStats.descDLife += dirtyLifeTime;
  3622.     } else if (cacheInfoPtr->flags & FSCACHE_LRU) {
  3623.     fscache_ExtraStats.lru++;
  3624.     fscache_ExtraStats.lruDLife += dirtyLifeTime;
  3625.     }
  3626.     return;
  3627.  
  3628. }
  3629.  
  3630.  
  3631.  
  3632. /*
  3633.  *----------------------------------------------------------------------
  3634.  *
  3635.  * Fscache_AddCleanStats --
  3636.  *
  3637.  *    Add extra sosp stats about clean blocks pulled from the lru list.
  3638.  *
  3639.  * Results:
  3640.  *    None.
  3641.  *
  3642.  * Side effects:
  3643.  *    Stats updated.
  3644.  *
  3645.  *----------------------------------------------------------------------
  3646.  */
  3647. void
  3648. Fscache_AddCleanStats(flags, blockPtr)
  3649.     unsigned int    flags;
  3650.     Fscache_Block    *blockPtr;
  3651. {
  3652.     time_t    now;
  3653.     time_t    lifeTime = 0;
  3654.     Boolean    unrefed = FALSE;
  3655.  
  3656.     now = Fsutil_TimeInSeconds();
  3657.     if (blockPtr->timeReferenced <= 0) {
  3658.     unrefed = TRUE;
  3659.     } else {
  3660.     lifeTime = now - blockPtr->timeReferenced;
  3661.     }
  3662.     if (lifeTime < 0) {
  3663.     unrefed = TRUE;
  3664.     }
  3665.  
  3666.     if (flags & FSCACHE_BLOCK_VM) {
  3667.     if (unrefed) {
  3668.         fscache_ExtraStats.unRcleanVm++;
  3669.     } else {
  3670.         fscache_ExtraStats.cleanVm++;
  3671.         fscache_ExtraStats.cVmLife += lifeTime;
  3672.     }
  3673.     } else if (flags & FSCACHE_BLOCK_SHRINK) {
  3674.     if (unrefed) {
  3675.         fscache_ExtraStats.unRcleanShrink++;
  3676.     } else {
  3677.         fscache_ExtraStats.cleanShrink++;
  3678.         fscache_ExtraStats.cShrinkLife += lifeTime;
  3679.     }
  3680.     } else {
  3681.     if (unrefed) {
  3682.         fscache_ExtraStats.unRcleanLru++;
  3683.     } else {
  3684.         fscache_ExtraStats.cleanLru++;
  3685.         fscache_ExtraStats.cLruLife += lifeTime;
  3686.     }
  3687.     }
  3688.     return;
  3689. }
  3690. #endif SOSP91
  3691.